mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-16 11:46:25 +01:00
new files from sakari, missed last time
git-svn-id: svn://localhost/ardour2/branches/3.0@3740 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
6ba5125e99
commit
f2b007195c
37 changed files with 9547 additions and 0 deletions
56
libs/ardour/ardour/audiofile_tagger.h
Normal file
56
libs/ardour/ardour/audiofile_tagger.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_audiofile_tagger_h__
|
||||
#define __ardour_audiofile_tagger_h__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <taglib/taglib.h>
|
||||
#include <taglib/tag.h>
|
||||
#include <taglib/xiphcomment.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class SessionMetadata;
|
||||
|
||||
/// Clas with static functions for tagging audiofiles
|
||||
class AudiofileTagger
|
||||
{
|
||||
public:
|
||||
|
||||
/* Tags file with metadata, return true on success */
|
||||
|
||||
static bool tag_file (string const & filename, SessionMetadata const & metadata);
|
||||
|
||||
private:
|
||||
|
||||
static bool tag_generic (TagLib::Tag & tag, SessionMetadata const & metadata);
|
||||
static bool tag_vorbis_comment (TagLib::Ogg::XiphComment & tag, SessionMetadata const & metadata);
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_audiofile_tagger_h__ */
|
||||
95
libs/ardour/ardour/broadcast_info.h
Normal file
95
libs/ardour/ardour/broadcast_info.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_broadcast_info_h__
|
||||
#define __ardour_broadcast_info_h__
|
||||
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
#include <sndfile.h>
|
||||
|
||||
#include <ardour/types.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class Session;
|
||||
|
||||
class BroadcastInfo
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct empty broadcast info
|
||||
BroadcastInfo ();
|
||||
~BroadcastInfo ();
|
||||
|
||||
/// Returns last error sring from libsndfile
|
||||
string get_error () const { return error; }
|
||||
|
||||
/* Convenience functions */
|
||||
|
||||
void set_from_session (Session const & session, int64_t time);
|
||||
|
||||
/* Reading */
|
||||
|
||||
bool load_from_file (string const & filename);
|
||||
bool load_from_file (SNDFILE* sf);
|
||||
|
||||
string get_description () const;
|
||||
int64_t get_time_reference () const;
|
||||
struct tm get_origination_time () const;
|
||||
string get_originator () const;
|
||||
string get_originator_ref () const;
|
||||
|
||||
/* Writing */
|
||||
|
||||
bool write_to_file (string const & filename);
|
||||
bool write_to_file (SNDFILE* sf);
|
||||
|
||||
void set_description (string const & desc);
|
||||
void set_time_reference (int64_t when);
|
||||
void set_origination_time (struct tm * now = 0); // if 0, use time generated at construction
|
||||
void set_originator (string const & str = "");
|
||||
void set_originator_ref (string const & str = "");
|
||||
|
||||
/* State info */
|
||||
|
||||
/// Returns true if a info has been succesfully loaded or anything has been manually set
|
||||
bool has_info () const { return _has_info; }
|
||||
|
||||
private:
|
||||
|
||||
SF_BROADCAST_INFO * info;
|
||||
struct tm _time;
|
||||
|
||||
void update_error ();
|
||||
string error;
|
||||
|
||||
bool _has_info;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_broadcast_info_h__ */
|
||||
138
libs/ardour/ardour/export_channel_configuration.h
Normal file
138
libs/ardour/ardour/export_channel_configuration.h
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_channel_configuration_h__
|
||||
#define __ardour_export_channel_configuration_h__
|
||||
|
||||
#include <set>
|
||||
#include <list>
|
||||
|
||||
#include <glibmm/ustring.h>
|
||||
#include <sigc++/signal.h>
|
||||
|
||||
#include <ardour/export_status.h>
|
||||
#include <ardour/ardour.h>
|
||||
|
||||
using Glib::ustring;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class ExportHandler;
|
||||
class AudioPort;
|
||||
class ExportChannel;
|
||||
class ExportFormatSpecification;
|
||||
class ExportFilename;
|
||||
class ExportProcessor;
|
||||
class ExportTimespan;
|
||||
|
||||
class ExportChannel : public std::set<AudioPort *>
|
||||
{
|
||||
public:
|
||||
ExportChannel ();
|
||||
~ExportChannel ();
|
||||
|
||||
void add_port (AudioPort * port) { if (port) { insert (port); } }
|
||||
void read_ports (float * data, nframes_t frames) const;
|
||||
};
|
||||
|
||||
class ExportChannelConfiguration
|
||||
{
|
||||
private:
|
||||
typedef boost::shared_ptr<ExportProcessor> ProcessorPtr;
|
||||
typedef boost::shared_ptr<ExportTimespan> TimespanPtr;
|
||||
typedef boost::shared_ptr<ExportFormatSpecification const> FormatPtr;
|
||||
typedef boost::shared_ptr<ExportFilename> FilenamePtr;
|
||||
|
||||
typedef std::pair<FormatPtr, FilenamePtr> FileConfig;
|
||||
typedef std::list<FileConfig> FileConfigList;
|
||||
|
||||
/// Struct for threading, acts like a pointer to a ExportChannelConfiguration
|
||||
struct WriterThread {
|
||||
WriterThread (ExportChannelConfiguration & channel_config) :
|
||||
channel_config (channel_config), running (false) {}
|
||||
|
||||
ExportChannelConfiguration * operator-> () { return &channel_config; }
|
||||
ExportChannelConfiguration & operator* () { return channel_config; }
|
||||
|
||||
ExportChannelConfiguration & channel_config;
|
||||
|
||||
pthread_t thread;
|
||||
bool running;
|
||||
};
|
||||
|
||||
private:
|
||||
friend class ExportElementFactory;
|
||||
ExportChannelConfiguration (ExportStatus & status);
|
||||
|
||||
public:
|
||||
~ExportChannelConfiguration ();
|
||||
|
||||
typedef boost::shared_ptr<ExportChannel const> ChannelPtr;
|
||||
typedef std::list<ChannelPtr> ChannelList;
|
||||
|
||||
ChannelList const & get_channels () { return channels; }
|
||||
bool all_channels_have_ports ();
|
||||
|
||||
ustring name () const { return _name; }
|
||||
void set_name (ustring name) { _name = name; }
|
||||
void set_split (bool value) { split = value; }
|
||||
|
||||
bool get_split () { return split; }
|
||||
uint32_t get_n_chans () { return channels.size(); }
|
||||
|
||||
void register_channel (ChannelPtr channel) { channels.push_back (channel); }
|
||||
void register_file_config (FormatPtr format, FilenamePtr filename) { file_configs.push_back (FileConfig (format, filename)); }
|
||||
|
||||
void clear_channels () { channels.clear (); }
|
||||
|
||||
/// Writes all files for this channel config @return true if a new thread was spawned
|
||||
bool write_files (boost::shared_ptr<ExportProcessor> new_processor);
|
||||
sigc::signal<void> FilesWritten;
|
||||
|
||||
// Tells the handler the necessary information for it to handle tempfiles
|
||||
void register_with_timespan (TimespanPtr timespan);
|
||||
|
||||
void unregister_all ();
|
||||
|
||||
private:
|
||||
|
||||
// processor has to be prepared before doing this.
|
||||
void write_file ();
|
||||
|
||||
/// The actual write files, needed for threading
|
||||
static void * _write_files (void *arg);
|
||||
WriterThread writer_thread;
|
||||
ProcessorPtr processor;
|
||||
ExportStatus & status;
|
||||
|
||||
bool files_written;
|
||||
|
||||
TimespanPtr timespan;
|
||||
ChannelList channels;
|
||||
FileConfigList file_configs;
|
||||
|
||||
bool split; // Split to mono files
|
||||
ustring _name;
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_channel_configuration_h__ */
|
||||
61
libs/ardour/ardour/export_failed.h
Normal file
61
libs/ardour/ardour/export_failed.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_failed_h__
|
||||
#define __ardour_export_failed_h__
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <pbd/error.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace PBD;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class ExportFailed : public std::exception
|
||||
{
|
||||
public:
|
||||
ExportFailed (std::string const & reason, std::string const & description) :
|
||||
reason (reason.c_str()),
|
||||
description (description.c_str())
|
||||
{
|
||||
error << string_compose (_("Export failed: %1"), reason) << endmsg;
|
||||
}
|
||||
|
||||
~ExportFailed () throw() { }
|
||||
|
||||
const char* what() const throw()
|
||||
{
|
||||
return description;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const char * reason;
|
||||
const char * description;
|
||||
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_failed_h__ */
|
||||
144
libs/ardour/ardour/export_file_io.h
Normal file
144
libs/ardour/ardour/export_file_io.h
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_file_io_h__
|
||||
#define __ardour_export_file_io_h__
|
||||
|
||||
#include <sndfile.h>
|
||||
|
||||
#include <ardour/graph.h>
|
||||
#include <ardour/types.h>
|
||||
#include <ardour/ardour.h>
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
/// Common part for all export file writers
|
||||
class ExportFileWriter
|
||||
{
|
||||
public:
|
||||
ExportFileWriter (string filename) : _filename (filename) {}
|
||||
virtual ~ExportFileWriter () {}
|
||||
|
||||
string filename () const { return _filename; }
|
||||
nframes_t position () const { return _position; }
|
||||
|
||||
void set_position (nframes_t position) { _position = position; }
|
||||
|
||||
protected:
|
||||
string _filename;
|
||||
nframes_t _position;
|
||||
};
|
||||
|
||||
/// Common interface for templated libsndfile writers
|
||||
class SndfileWriterBase : public ExportFileWriter
|
||||
{
|
||||
public:
|
||||
SndfileWriterBase (int channels, nframes_t samplerate, int format, string const & path);
|
||||
virtual ~SndfileWriterBase ();
|
||||
|
||||
SNDFILE * get_sndfile () const { return sndfile; }
|
||||
|
||||
protected:
|
||||
SF_INFO sf_info;
|
||||
SNDFILE * sndfile;
|
||||
};
|
||||
|
||||
|
||||
/// Template parameter specific parts of sndfile writer
|
||||
template <typename T>
|
||||
class SndfileWriter : public SndfileWriterBase, public GraphSink<T>
|
||||
{
|
||||
public:
|
||||
SndfileWriter (int channels, nframes_t samplerate, int format, string const & path);
|
||||
virtual ~SndfileWriter () {}
|
||||
|
||||
nframes_t write (T * data, nframes_t frames);
|
||||
|
||||
protected:
|
||||
sf_count_t (*write_func)(SNDFILE *, const T *, sf_count_t);
|
||||
|
||||
private:
|
||||
void init (); // Inits write function
|
||||
};
|
||||
|
||||
/// Writes and reads a RAW tempfile (file aquired with tmpfile())
|
||||
class ExportTempFile : public SndfileWriter<float>, public GraphSource<float>
|
||||
{
|
||||
public:
|
||||
ExportTempFile (uint32_t channels, nframes_t samplerate);
|
||||
~ExportTempFile () {}
|
||||
|
||||
/// Causes the file to be read from the beginning again
|
||||
void reset_read () { reading = false; }
|
||||
nframes_t read (float * data, nframes_t frames);
|
||||
|
||||
/* Silence management */
|
||||
|
||||
nframes_t trim_beginning (bool yn = true);
|
||||
nframes_t trim_end (bool yn = true);
|
||||
|
||||
void set_silence_beginning (nframes_t frames);
|
||||
void set_silence_end (nframes_t frames);
|
||||
|
||||
private:
|
||||
/* File access */
|
||||
|
||||
sf_count_t get_length ();
|
||||
sf_count_t get_position ();
|
||||
sf_count_t get_read_position (); // get position seems to default to the write pointer
|
||||
sf_count_t locate_to (nframes_t frames);
|
||||
sf_count_t _read (float * data, nframes_t frames);
|
||||
|
||||
uint32_t channels;
|
||||
bool reading;
|
||||
|
||||
/* Silence related */
|
||||
|
||||
/* start and end are used by read() */
|
||||
|
||||
nframes_t start;
|
||||
nframes_t end;
|
||||
|
||||
/* these are the silence processing results and state */
|
||||
|
||||
void process_beginning ();
|
||||
void process_end ();
|
||||
|
||||
bool beginning_processed;
|
||||
bool end_processed;
|
||||
|
||||
nframes_t silent_frames_beginning;
|
||||
nframes_t silent_frames_end;
|
||||
|
||||
/* Silence to add to start and end */
|
||||
|
||||
nframes_t silence_beginning;
|
||||
nframes_t silence_end;
|
||||
|
||||
/* Takes care that the end postion gets set at some stage */
|
||||
|
||||
bool end_set;
|
||||
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_file_io_h__ */
|
||||
140
libs/ardour/ardour/export_filename.h
Normal file
140
libs/ardour/ardour/export_filename.h
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_filename_h__
|
||||
#define __ardour_export_filename_h__
|
||||
|
||||
#include <glibmm/ustring.h>
|
||||
#include <pbd/statefuldestructible.h>
|
||||
|
||||
#include <ardour/session.h>
|
||||
|
||||
using Glib::ustring;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class Session;
|
||||
class ExportTimespan;
|
||||
class ExportChannelConfiguration;
|
||||
class ExportFormatSpecification;
|
||||
|
||||
class ExportFilename {
|
||||
private:
|
||||
|
||||
typedef boost::shared_ptr<ExportTimespan> TimespanPtr;
|
||||
typedef boost::shared_ptr<ExportChannelConfiguration> ChannelConfigPtr;
|
||||
typedef boost::shared_ptr<ExportFormatSpecification const> FormatPtr;
|
||||
|
||||
public:
|
||||
|
||||
enum DateFormat {
|
||||
D_None,
|
||||
D_ISO, // ISO 8601 full date
|
||||
D_ISOShortY, // Like ISO 8601, but short year representation
|
||||
D_BE, // big endian (no deliminator)
|
||||
D_BEShortY // big endian short year representation
|
||||
};
|
||||
|
||||
enum TimeFormat {
|
||||
T_None,
|
||||
T_NoDelim,
|
||||
T_Delim
|
||||
};
|
||||
|
||||
private:
|
||||
friend class ExportElementFactory;
|
||||
ExportFilename (Session & session);
|
||||
|
||||
public:
|
||||
/* Serialization */
|
||||
|
||||
XMLNode & get_state ();
|
||||
int set_state (const XMLNode &);
|
||||
|
||||
/* data access */
|
||||
|
||||
ustring get_path (FormatPtr format) const;
|
||||
ustring get_folder () const { return folder; }
|
||||
|
||||
TimeFormat get_time_format () const { return time_format; }
|
||||
DateFormat get_date_format () const { return date_format; }
|
||||
ustring get_time_format_str (TimeFormat format) const;
|
||||
ustring get_date_format_str (DateFormat format) const;
|
||||
|
||||
ustring get_label () const { return label; }
|
||||
uint32_t get_revision () const { return revision; }
|
||||
|
||||
/* data modification */
|
||||
|
||||
void set_time_format (TimeFormat format);
|
||||
void set_date_format (DateFormat format);
|
||||
void set_label (ustring value);
|
||||
void set_revision (uint32_t value) { revision = value; }
|
||||
void set_channel (uint32_t value) { channel = value; }
|
||||
bool set_folder (ustring path);
|
||||
|
||||
void set_timespan (TimespanPtr ts) { timespan = ts; }
|
||||
void set_channel_config (ChannelConfigPtr cc) { channel_config = cc; }
|
||||
|
||||
/* public members */
|
||||
|
||||
bool include_label;
|
||||
bool include_session;
|
||||
bool include_revision;
|
||||
bool include_channel_config;
|
||||
bool include_channel;
|
||||
bool include_timespan;
|
||||
bool include_time;
|
||||
bool include_date;
|
||||
|
||||
private:
|
||||
|
||||
Session & session;
|
||||
|
||||
ustring label;
|
||||
uint32_t revision;
|
||||
uint32_t channel;
|
||||
|
||||
ustring folder;
|
||||
|
||||
DateFormat date_format;
|
||||
TimeFormat time_format;
|
||||
|
||||
ustring get_formatted_time (ustring const & format) const;
|
||||
struct tm * time_struct; // Due to static allocation no destructor or copy-ctor is needed because of this
|
||||
|
||||
TimespanPtr timespan;
|
||||
ChannelConfigPtr channel_config;
|
||||
|
||||
/* Serialization helpers */
|
||||
|
||||
typedef std::pair<bool, ustring> FieldPair;
|
||||
|
||||
void add_field (XMLNode * node, ustring const & name, bool enabled, ustring const & value = "");
|
||||
FieldPair get_field (XMLNode const & node, ustring const & name);
|
||||
FieldPair analyse_folder ();
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_filename_h__ */
|
||||
199
libs/ardour/ardour/export_format_base.h
Normal file
199
libs/ardour/ardour/export_format_base.h
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_format_base_h__
|
||||
#define __ardour_export_format_base_h__
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <glibmm/ustring.h>
|
||||
|
||||
#include <sndfile.h>
|
||||
#include <samplerate.h>
|
||||
#include <ardour/gdither_types.h>
|
||||
#include <ardour/ardour.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class HasSampleFormat;
|
||||
|
||||
class ExportFormatBase {
|
||||
public:
|
||||
|
||||
enum Type {
|
||||
T_None = 0,
|
||||
T_Sndfile
|
||||
};
|
||||
|
||||
enum FormatId {
|
||||
F_None = 0,
|
||||
F_WAV = SF_FORMAT_WAV,
|
||||
F_W64 = SF_FORMAT_W64,
|
||||
F_AIFF = SF_FORMAT_AIFF,
|
||||
F_AU = SF_FORMAT_AU,
|
||||
F_IRCAM = SF_FORMAT_IRCAM,
|
||||
F_RAW = SF_FORMAT_RAW,
|
||||
F_FLAC = SF_FORMAT_FLAC,
|
||||
F_Ogg = SF_FORMAT_OGG
|
||||
};
|
||||
|
||||
enum Endianness {
|
||||
E_FileDefault = SF_ENDIAN_FILE, /* Default file endian-ness. */
|
||||
E_Little = SF_ENDIAN_LITTLE, /* Force little endian-ness. */
|
||||
E_Big = SF_ENDIAN_BIG, /* Force big endian-ness. */
|
||||
E_Cpu = SF_ENDIAN_CPU /* Force CPU endian-ness. */
|
||||
};
|
||||
|
||||
enum SampleFormat {
|
||||
SF_None = 0,
|
||||
SF_8 = SF_FORMAT_PCM_S8,
|
||||
SF_16 = SF_FORMAT_PCM_16,
|
||||
SF_24 = SF_FORMAT_PCM_24,
|
||||
SF_32 = SF_FORMAT_PCM_32,
|
||||
SF_U8 = SF_FORMAT_PCM_U8,
|
||||
SF_Float = SF_FORMAT_FLOAT,
|
||||
SF_Double = SF_FORMAT_DOUBLE,
|
||||
SF_Vorbis = SF_FORMAT_VORBIS
|
||||
};
|
||||
|
||||
enum DitherType {
|
||||
D_None = GDitherNone,
|
||||
D_Rect = GDitherRect,
|
||||
D_Tri = GDitherTri,
|
||||
D_Shaped = GDitherShaped
|
||||
};
|
||||
|
||||
enum Quality {
|
||||
Q_None = 0,
|
||||
Q_Any,
|
||||
Q_LosslessLinear,
|
||||
Q_LosslessCompression,
|
||||
Q_LossyCompression
|
||||
};
|
||||
|
||||
enum SampleRate {
|
||||
SR_None = 0,
|
||||
SR_22_05 = 220500,
|
||||
SR_44_1 = 44100,
|
||||
SR_48 = 48000,
|
||||
SR_88_2 = 88200,
|
||||
SR_96 = 96000,
|
||||
SR_192 = 192000
|
||||
};
|
||||
|
||||
enum SRCQuality {
|
||||
SRC_SincBest = SRC_SINC_BEST_QUALITY,
|
||||
SRC_SincMedium = SRC_SINC_MEDIUM_QUALITY,
|
||||
SRC_SincFast = SRC_SINC_FASTEST,
|
||||
SRC_ZeroOrderHold = SRC_ZERO_ORDER_HOLD,
|
||||
SRC_Linear = SRC_LINEAR
|
||||
};
|
||||
|
||||
/// Class for managing selection and compatibility states
|
||||
class SelectableCompatible {
|
||||
public:
|
||||
SelectableCompatible () :
|
||||
_selected (false), _compatible (true) { }
|
||||
~SelectableCompatible () {}
|
||||
|
||||
sigc::signal<void, bool> SelectChanged;
|
||||
sigc::signal<void, bool> CompatibleChanged;
|
||||
|
||||
bool selected () const { return _selected; }
|
||||
bool compatible () const { return _compatible; }
|
||||
Glib::ustring name () const { return _name; }
|
||||
|
||||
void set_selected (bool value);
|
||||
void set_compatible (bool value);
|
||||
|
||||
protected:
|
||||
void set_name (Glib::ustring name) { _name = name; }
|
||||
|
||||
private:
|
||||
bool _selected;
|
||||
bool _compatible;
|
||||
|
||||
Glib::ustring _name;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
ExportFormatBase ();
|
||||
ExportFormatBase (ExportFormatBase const & other);
|
||||
|
||||
virtual ~ExportFormatBase ();
|
||||
|
||||
boost::shared_ptr<ExportFormatBase> get_intersection (ExportFormatBase const & other) const;
|
||||
boost::shared_ptr<ExportFormatBase> get_difference (ExportFormatBase const & other) const;
|
||||
boost::shared_ptr<ExportFormatBase> get_union (ExportFormatBase const & other) const;
|
||||
|
||||
bool endiannesses_empty () const { return endiannesses.empty (); }
|
||||
bool sample_formats_empty () const { return sample_formats.empty (); }
|
||||
bool sample_rates_empty () const { return sample_rates.empty (); }
|
||||
bool formats_empty () const { return format_ids.empty (); }
|
||||
bool qualities_empty () const { return qualities.empty (); }
|
||||
|
||||
bool has_endianness (Endianness endianness) const { return endiannesses.find (endianness) != endiannesses.end() ; }
|
||||
bool has_sample_format (SampleFormat format) const { return sample_formats.find (format) != sample_formats.end(); }
|
||||
bool has_sample_rate (SampleRate rate) const { return sample_rates.find (rate) != sample_rates.end(); }
|
||||
bool has_format (FormatId format) const { return format_ids.find (format) != format_ids.end(); }
|
||||
bool has_quality (Quality quality) const { return qualities.find (quality) != qualities.end(); }
|
||||
|
||||
void set_extension (Glib::ustring const & extension) { _extension = extension; }
|
||||
Glib::ustring const & extension () const { return _extension; }
|
||||
|
||||
protected:
|
||||
|
||||
friend class HasSampleFormat;
|
||||
typedef std::set<SampleFormat> SampleFormatSet;
|
||||
SampleFormatSet sample_formats;
|
||||
|
||||
protected:
|
||||
typedef std::set<Endianness> EndianSet;
|
||||
typedef std::set<SampleRate> SampleRateSet;
|
||||
typedef std::set<FormatId> FormatSet;
|
||||
typedef std::set<Quality> QualitySet;
|
||||
|
||||
EndianSet endiannesses;
|
||||
SampleRateSet sample_rates;
|
||||
FormatSet format_ids;
|
||||
QualitySet qualities;
|
||||
|
||||
private:
|
||||
|
||||
Glib::ustring _extension;
|
||||
|
||||
enum SetOperation {
|
||||
SetUnion,
|
||||
SetDifference,
|
||||
SetIntersection
|
||||
};
|
||||
|
||||
boost::shared_ptr<ExportFormatBase> do_set_operation (ExportFormatBase const & other, SetOperation operation) const;
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_format_base_h__ */
|
||||
57
libs/ardour/ardour/export_format_compatibility.h
Normal file
57
libs/ardour/ardour/export_format_compatibility.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_format_compatibility_h__
|
||||
#define __ardour_export_format_compatibility_h__
|
||||
|
||||
#include <ardour/export_format_base.h>
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
/// Allows adding to all sets. A format should be able to test if it is compatible with this
|
||||
class ExportFormatCompatibility : public ExportFormatBase, public ExportFormatBase::SelectableCompatible {
|
||||
private:
|
||||
|
||||
public:
|
||||
ExportFormatCompatibility (Glib::ustring name)
|
||||
{
|
||||
set_name (name);
|
||||
sample_formats.insert (SF_None);
|
||||
sample_rates.insert (SR_None);
|
||||
format_ids.insert (F_None);
|
||||
qualities.insert (Q_None);
|
||||
}
|
||||
|
||||
~ExportFormatCompatibility () {};
|
||||
|
||||
ExportFormatCompatibility (ExportFormatBase const & other) :
|
||||
ExportFormatBase (other) {}
|
||||
|
||||
void add_endianness (Endianness endianness) { endiannesses.insert (endianness); }
|
||||
void add_sample_format (SampleFormat format) { sample_formats.insert (format); }
|
||||
void add_sample_rate (SampleRate rate) { sample_rates.insert (rate); }
|
||||
void add_format_id (FormatId id) { format_ids.insert (id); }
|
||||
void add_quality (Quality quality) { qualities.insert (quality); }
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_format_compatibility_h__ */
|
||||
176
libs/ardour/ardour/export_format_manager.h
Normal file
176
libs/ardour/ardour/export_format_manager.h
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_format_manager_h__
|
||||
#define __ardour_export_format_manager_h__
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/weak_ptr.hpp>
|
||||
#include <sigc++/signal.h>
|
||||
#include <sigc++/trackable.h>
|
||||
|
||||
#include <ardour/export_formats.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class ExportFormat;
|
||||
class ExportFormatCompatibility;
|
||||
class ExportFormatSpecification;
|
||||
class AnyTime;
|
||||
|
||||
class ExportFormatManager : public sigc::trackable
|
||||
{
|
||||
public:
|
||||
|
||||
typedef boost::shared_ptr<ExportFormatCompatibility> CompatPtr;
|
||||
typedef boost::weak_ptr<ExportFormatCompatibility> WeakCompatPtr;
|
||||
typedef std::list<CompatPtr> CompatList;
|
||||
|
||||
typedef boost::shared_ptr<ExportFormat> FormatPtr;
|
||||
typedef boost::weak_ptr<ExportFormat> WeakFormatPtr;
|
||||
typedef std::list<FormatPtr> FormatList;
|
||||
|
||||
typedef HasSampleFormat::SampleFormatPtr SampleFormatPtr;
|
||||
typedef HasSampleFormat::SampleFormatList SampleFormatList;
|
||||
typedef HasSampleFormat::WeakSampleFormatPtr WeakSampleFormatPtr;
|
||||
|
||||
typedef HasSampleFormat::DitherTypePtr DitherTypePtr;
|
||||
typedef HasSampleFormat::WeakDitherTypePtr WeakDitherTypePtr;
|
||||
|
||||
typedef boost::shared_ptr<ExportFormatSpecification> SpecPtr;
|
||||
typedef boost::shared_ptr<ExportFormatBase> FormatBasePtr;
|
||||
|
||||
/* Quality states */
|
||||
|
||||
class QualityState : public ExportFormatBase::SelectableCompatible {
|
||||
public:
|
||||
QualityState (ExportFormatBase::Quality quality, Glib::ustring name) :
|
||||
quality (quality) { set_name (name); }
|
||||
ExportFormatBase::Quality quality;
|
||||
};
|
||||
typedef boost::shared_ptr<QualityState> QualityPtr;
|
||||
typedef boost::weak_ptr<QualityState> WeakQualityPtr;
|
||||
typedef std::list<QualityPtr> QualityList;
|
||||
|
||||
/* Sample rate states */
|
||||
|
||||
class SampleRateState : public ExportFormatBase::SelectableCompatible {
|
||||
public:
|
||||
SampleRateState (ExportFormatBase::SampleRate rate, Glib::ustring name) :
|
||||
rate (rate) { set_name (name); }
|
||||
ExportFormatBase::SampleRate rate;
|
||||
};
|
||||
typedef boost::shared_ptr<SampleRateState> SampleRatePtr;
|
||||
typedef boost::weak_ptr<SampleRateState> WeakSampleRatePtr;
|
||||
typedef std::list<SampleRatePtr> SampleRateList;
|
||||
|
||||
public:
|
||||
|
||||
explicit ExportFormatManager (SpecPtr specification);
|
||||
~ExportFormatManager ();
|
||||
|
||||
/* Signals */
|
||||
|
||||
sigc::signal<void, bool> CompleteChanged;
|
||||
|
||||
/* Access to lists */
|
||||
|
||||
CompatList const & get_compatibilities () { return compatibilities; }
|
||||
QualityList const & get_qualities () { return qualities; }
|
||||
FormatList const & get_formats () { return formats; }
|
||||
SampleRateList const & get_sample_rates () { return sample_rates; }
|
||||
|
||||
/* Non interactive selections */
|
||||
|
||||
void set_name (Glib::ustring name);
|
||||
|
||||
void select_src_quality (ExportFormatBase::SRCQuality value);
|
||||
void select_trim_beginning (bool value);
|
||||
void select_silence_beginning (AnyTime const & time);
|
||||
void select_trim_end (bool value);
|
||||
void select_silence_end (AnyTime const & time);
|
||||
void select_normalize (bool value);
|
||||
void select_normalize_target (float value);
|
||||
void select_tagging (bool tag);
|
||||
|
||||
private:
|
||||
|
||||
void init_compatibilities ();
|
||||
void init_qualities ();
|
||||
void init_formats ();
|
||||
void init_sample_rates ();
|
||||
|
||||
void add_compatibility (CompatPtr ptr);
|
||||
void add_quality (QualityPtr ptr);
|
||||
void add_format (FormatPtr ptr);
|
||||
void add_sample_rate (SampleRatePtr ptr);
|
||||
|
||||
/* Connected to signals */
|
||||
|
||||
void change_compatibility_selection (bool select, WeakCompatPtr const & compat);
|
||||
void change_quality_selection (bool select, WeakQualityPtr const & quality);
|
||||
void change_format_selection (bool select, WeakFormatPtr const & format);
|
||||
void change_sample_rate_selection (bool select, WeakSampleRatePtr const & rate);
|
||||
|
||||
void change_sample_format_selection (bool select, WeakSampleFormatPtr const & format);
|
||||
void change_dither_type_selection (bool select, WeakDitherTypePtr const & type);
|
||||
|
||||
/* Do actual selection */
|
||||
|
||||
void select_compatibility (WeakCompatPtr const & compat);
|
||||
void select_quality (QualityPtr const & quality);
|
||||
void select_format (FormatPtr const & format);
|
||||
void select_sample_rate (SampleRatePtr const & rate);
|
||||
|
||||
void select_sample_format (SampleFormatPtr const & format);
|
||||
void select_dither_type (DitherTypePtr const & type);
|
||||
|
||||
bool pending_selection_change;
|
||||
void selection_changed ();
|
||||
|
||||
/* Formats and compatibilities */
|
||||
|
||||
QualityPtr get_selected_quality ();
|
||||
FormatPtr get_selected_format ();
|
||||
SampleRatePtr get_selected_sample_rate ();
|
||||
|
||||
SampleFormatPtr get_selected_sample_format ();
|
||||
|
||||
FormatBasePtr get_compatibility_intersection ();
|
||||
|
||||
FormatBasePtr universal_set;
|
||||
SpecPtr current_selection;
|
||||
|
||||
CompatList compatibilities;
|
||||
QualityList qualities;
|
||||
FormatList formats;
|
||||
SampleRateList sample_rates;
|
||||
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_format_manager_h__ */
|
||||
185
libs/ardour/ardour/export_format_specification.h
Normal file
185
libs/ardour/ardour/export_format_specification.h
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_format_specification_h__
|
||||
#define __ardour_export_format_specification_h__
|
||||
|
||||
#include <glibmm/ustring.h>
|
||||
|
||||
#include <ardour/types.h>
|
||||
#include <ardour/export_format_base.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
class XMLNode;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class ExportFormat;
|
||||
class ExportFormatCompatibility;
|
||||
class Session;
|
||||
|
||||
class ExportFormatSpecification : public ExportFormatBase {
|
||||
|
||||
private:
|
||||
|
||||
/* Time struct for keeping time formats as close as possible to what was requested */
|
||||
|
||||
struct Time : public AnyTime {
|
||||
Time (Session & session) : AnyTime (), session (session) {}
|
||||
Time & operator= (AnyTime const & other);
|
||||
|
||||
nframes_t get_frames (nframes_t target_rate) const;
|
||||
|
||||
/* Serialization */
|
||||
|
||||
XMLNode & get_state ();
|
||||
int set_state (const XMLNode & node);
|
||||
|
||||
private:
|
||||
Session & session;
|
||||
};
|
||||
|
||||
private:
|
||||
friend class ExportElementFactory;
|
||||
explicit ExportFormatSpecification (Session & s);
|
||||
ExportFormatSpecification (Session & s, XMLNode const & state);
|
||||
|
||||
public:
|
||||
ExportFormatSpecification (ExportFormatSpecification const & other);
|
||||
~ExportFormatSpecification ();
|
||||
|
||||
/* compatibility */
|
||||
|
||||
bool is_compatible_with (ExportFormatCompatibility const & compatibility) const;
|
||||
bool is_complete () const;
|
||||
|
||||
/* Modifying functions */
|
||||
|
||||
void set_format (boost::shared_ptr<ExportFormat> format);
|
||||
|
||||
void set_name (Glib::ustring const & name) { _name = name; }
|
||||
|
||||
void set_type (Type type) { _type = type; }
|
||||
void set_format_id (FormatId value) { format_ids.clear(); format_ids.insert (value); }
|
||||
void set_endianness (Endianness value) { endiannesses.clear(); endiannesses.insert (value); }
|
||||
void set_sample_format (SampleFormat value) { sample_formats.clear(); sample_formats.insert (value); }
|
||||
void set_sample_rate (SampleRate value) { sample_rates.clear(); sample_rates.insert (value); }
|
||||
void set_quality (Quality value) { qualities.clear(); qualities.insert (value); }
|
||||
|
||||
void set_dither_type (DitherType value) { _dither_type = value; }
|
||||
void set_src_quality (SRCQuality value) { _src_quality = value; }
|
||||
void set_trim_beginning (bool value) { _trim_beginning = value; }
|
||||
void set_trim_end (bool value) { _trim_end = value; }
|
||||
void set_normalize (bool value) { _normalize = value; }
|
||||
void set_normalize_target (float value) { _normalize_target = value; }
|
||||
|
||||
void set_tag (bool tag_it) { _tag = tag_it; }
|
||||
|
||||
void set_silence_beginning (AnyTime const & value) { _silence_beginning = value; }
|
||||
void set_silence_end (AnyTime const & value) { _silence_end = value; }
|
||||
|
||||
/* Accessing functions */
|
||||
|
||||
uint32_t id () { return _id; }
|
||||
Glib::ustring const & name () const { return _name; }
|
||||
Glib::ustring description ();
|
||||
|
||||
bool has_broadcast_info () const { return _has_broadcast_info; }
|
||||
uint32_t channel_limit () const { return _channel_limit; }
|
||||
Glib::ustring format_name () const { return _format_name; }
|
||||
|
||||
Type type () const { return _type; }
|
||||
FormatId format_id () const { return *format_ids.begin(); }
|
||||
Endianness endianness () const { return *endiannesses.begin(); }
|
||||
SampleFormat sample_format () const { return *sample_formats.begin(); }
|
||||
SampleRate sample_rate () const { return *sample_rates.begin(); }
|
||||
Quality quality () const { return *qualities.begin(); }
|
||||
|
||||
DitherType dither_type () const { return _dither_type; }
|
||||
SRCQuality src_quality () const { return _src_quality; }
|
||||
bool trim_beginning () const { return _trim_beginning; }
|
||||
bool trim_end () const { return _trim_end; }
|
||||
bool normalize () const { return _normalize; }
|
||||
float normalize_target () const { return _normalize_target; }
|
||||
|
||||
bool tag () const { return _tag && supports_tagging; }
|
||||
|
||||
nframes_t silence_beginning () const { return _silence_beginning.get_frames (sample_rate()); }
|
||||
nframes_t silence_end () const { return _silence_end.get_frames (sample_rate()); }
|
||||
|
||||
AnyTime silence_beginning_time () const { return _silence_beginning; }
|
||||
AnyTime silence_end_time () const { return _silence_end; }
|
||||
|
||||
/* Serialization */
|
||||
|
||||
XMLNode & get_state ();
|
||||
int set_state (const XMLNode & root);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
Session & session;
|
||||
|
||||
/* The variables below do not have setters (usually set via set_format) */
|
||||
|
||||
Glib::ustring _format_name;
|
||||
bool has_sample_format;
|
||||
bool supports_tagging;
|
||||
bool _has_broadcast_info;
|
||||
uint32_t _channel_limit;
|
||||
|
||||
/* The variables below have getters and setters */
|
||||
|
||||
Glib::ustring _name;
|
||||
uint32_t _id;
|
||||
|
||||
Type _type;
|
||||
DitherType _dither_type;
|
||||
SRCQuality _src_quality;
|
||||
|
||||
bool _tag;
|
||||
|
||||
bool _trim_beginning;
|
||||
Time _silence_beginning;
|
||||
bool _trim_end;
|
||||
Time _silence_end;
|
||||
|
||||
bool _normalize;
|
||||
float _normalize_target;
|
||||
|
||||
/* serialization helpers */
|
||||
|
||||
void add_option (XMLNode * node, std::string const & name, std::string const & value);
|
||||
std::string get_option (XMLNode const * node, std::string const & name);
|
||||
|
||||
/*** Static stuff for id management, ExportElementFactory will have access to these ***/
|
||||
|
||||
static void init_counter (uint32_t val) { if (val > _counter) { _counter = val; } }
|
||||
static uint32_t counter () { return _counter; }
|
||||
|
||||
static uint32_t _counter;
|
||||
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_format_specification_h__ */
|
||||
206
libs/ardour/ardour/export_formats.h
Normal file
206
libs/ardour/ardour/export_formats.h
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_formats_h__
|
||||
#define __ardour_export_formats_h__
|
||||
|
||||
#include <ardour/export_format_base.h>
|
||||
#include <ardour/export_format_compatibility.h>
|
||||
|
||||
#include <list>
|
||||
#include <boost/weak_ptr.hpp>
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class HasSampleFormat;
|
||||
|
||||
/// Base class for formats
|
||||
class ExportFormat : public ExportFormatBase, public ExportFormatBase::SelectableCompatible {
|
||||
|
||||
public:
|
||||
ExportFormat () {};
|
||||
~ExportFormat () {};
|
||||
|
||||
virtual bool set_compatibility_state (ExportFormatCompatibility const & compatibility) = 0;
|
||||
virtual Type get_type () const = 0;
|
||||
|
||||
FormatId get_format_id () const { return *format_ids.begin(); }
|
||||
Quality get_quality () const { return *qualities.begin(); }
|
||||
|
||||
bool has_sample_format ();
|
||||
bool sample_format_is_compatible (SampleFormat format) const;
|
||||
|
||||
/* If the format has a specific sample format, this function should be overriden
|
||||
* if the format has a selectable sample format, do not override this!
|
||||
*/
|
||||
|
||||
virtual SampleFormat get_explicit_sample_format () const { return SF_None; }
|
||||
|
||||
/* If the above is not overriden, this one should be */
|
||||
|
||||
virtual ExportFormat::SampleFormat default_sample_format () const { return SF_None; }
|
||||
|
||||
/* If the format has a channel count limit, override this */
|
||||
|
||||
virtual uint32_t get_channel_limit () const { return 256; }
|
||||
|
||||
/* If the format can be tagged with metadata override this */
|
||||
|
||||
virtual bool supports_tagging () const { return false; }
|
||||
|
||||
/* If the format contains broadcast info override this */
|
||||
|
||||
virtual bool has_broadcast_info () const { return false; }
|
||||
|
||||
protected:
|
||||
|
||||
void add_sample_rate (SampleRate rate) { sample_rates.insert (rate); }
|
||||
void add_endianness (Endianness endianness) { endiannesses.insert (endianness); }
|
||||
|
||||
void set_format_id (FormatId id) { format_ids.clear (); format_ids.insert (id); }
|
||||
void set_quality (Quality value) { qualities.clear(); qualities.insert (value); }
|
||||
};
|
||||
|
||||
/// Class to be inherited by export formats that have a selectable sample format
|
||||
class HasSampleFormat {
|
||||
public:
|
||||
|
||||
class SampleFormatState : public ExportFormatBase::SelectableCompatible {
|
||||
public:
|
||||
SampleFormatState (ExportFormatBase::SampleFormat format, Glib::ustring name) :
|
||||
format (format) { set_name (name); }
|
||||
|
||||
ExportFormatBase::SampleFormat format;
|
||||
};
|
||||
|
||||
class DitherTypeState : public ExportFormatBase::SelectableCompatible {
|
||||
public:
|
||||
DitherTypeState (ExportFormatBase::DitherType type, Glib::ustring name) :
|
||||
type (type) { set_name (name); }
|
||||
|
||||
ExportFormatBase::DitherType type;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<SampleFormatState> SampleFormatPtr;
|
||||
typedef boost::weak_ptr<SampleFormatState> WeakSampleFormatPtr;
|
||||
typedef std::list<SampleFormatPtr> SampleFormatList;
|
||||
|
||||
typedef boost::shared_ptr<DitherTypeState> DitherTypePtr;
|
||||
typedef boost::weak_ptr<DitherTypeState> WeakDitherTypePtr;
|
||||
typedef std::list<DitherTypePtr> DitherTypeList;
|
||||
|
||||
public:
|
||||
|
||||
HasSampleFormat (ExportFormatBase::SampleFormatSet & sample_formats);
|
||||
virtual ~HasSampleFormat () {};
|
||||
|
||||
void add_sample_format (ExportFormatBase::SampleFormat format);
|
||||
|
||||
SampleFormatList const & get_sample_formats () const { return sample_format_states; }
|
||||
DitherTypeList const & get_dither_types () const { return dither_type_states; }
|
||||
|
||||
SampleFormatPtr get_selected_sample_format ();
|
||||
DitherTypePtr get_selected_dither_type ();
|
||||
|
||||
/* Proxies for signals from sample formats and dither types */
|
||||
|
||||
sigc::signal<void, bool, WeakSampleFormatPtr> SampleFormatSelectChanged;
|
||||
sigc::signal<void, bool, WeakSampleFormatPtr> SampleFormatCompatibleChanged;
|
||||
|
||||
sigc::signal<void, bool, WeakDitherTypePtr> DitherTypeSelectChanged;
|
||||
sigc::signal<void, bool, WeakDitherTypePtr> DitherTypeCompatibleChanged;
|
||||
|
||||
static string get_sample_format_name (ExportFormatBase::SampleFormat format);
|
||||
|
||||
protected:
|
||||
/* State lists */
|
||||
|
||||
DitherTypeList dither_type_states;
|
||||
SampleFormatList sample_format_states;
|
||||
|
||||
private:
|
||||
/* Connected to signals */
|
||||
|
||||
void add_dither_type (ExportFormatBase::DitherType type, Glib::ustring name);
|
||||
void update_sample_format_selection (bool);
|
||||
void update_dither_type_selection (bool);
|
||||
|
||||
/* Reference to ExportFormatBase::sample_formats */
|
||||
ExportFormatBase::SampleFormatSet & _sample_formats;
|
||||
};
|
||||
|
||||
class ExportFormatLinear : public ExportFormat, public HasSampleFormat {
|
||||
public:
|
||||
|
||||
ExportFormatLinear (Glib::ustring name, FormatId format_id);
|
||||
~ExportFormatLinear () {};
|
||||
|
||||
bool set_compatibility_state (ExportFormatCompatibility const & compatibility);
|
||||
Type get_type () const { return T_Sndfile; }
|
||||
|
||||
void add_endianness (Endianness endianness) { endiannesses.insert (endianness); }
|
||||
|
||||
void set_default_sample_format (SampleFormat sf) { _default_sample_format = sf; }
|
||||
SampleFormat default_sample_format () const { return _default_sample_format; }
|
||||
|
||||
protected:
|
||||
SampleFormat _default_sample_format;
|
||||
};
|
||||
|
||||
class ExportFormatOggVorbis : public ExportFormat {
|
||||
public:
|
||||
ExportFormatOggVorbis ();
|
||||
~ExportFormatOggVorbis () {};
|
||||
|
||||
bool set_compatibility_state (ExportFormatCompatibility const & compatibility);
|
||||
Type get_type () const { return T_Sndfile; }
|
||||
SampleFormat get_explicit_sample_format () const { return SF_Vorbis; }
|
||||
virtual bool supports_tagging () const { return true; }
|
||||
};
|
||||
|
||||
class ExportFormatFLAC : public ExportFormat, public HasSampleFormat {
|
||||
public:
|
||||
ExportFormatFLAC ();
|
||||
~ExportFormatFLAC () {};
|
||||
|
||||
bool set_compatibility_state (ExportFormatCompatibility const & compatibility);
|
||||
Type get_type () const { return T_Sndfile; }
|
||||
|
||||
uint32_t get_channel_limit () const { return 8; }
|
||||
SampleFormat default_sample_format () const { return SF_16; }
|
||||
virtual bool supports_tagging () const { return true; }
|
||||
};
|
||||
|
||||
class ExportFormatBWF : public ExportFormat, public HasSampleFormat {
|
||||
public:
|
||||
ExportFormatBWF ();
|
||||
~ExportFormatBWF () {};
|
||||
|
||||
bool set_compatibility_state (ExportFormatCompatibility const & compatibility);
|
||||
Type get_type () const { return T_Sndfile; }
|
||||
|
||||
SampleFormat default_sample_format () const { return SF_16; }
|
||||
virtual bool has_broadcast_info () const { return true; }
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_formats__ */
|
||||
182
libs/ardour/ardour/export_handler.h
Normal file
182
libs/ardour/ardour/export_handler.h
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_handler_h__
|
||||
#define __ardour_export_handler_h__
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <fstream>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <ardour/ardour.h>
|
||||
#include <ardour/session.h>
|
||||
#include <ardour/types.h>
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class ExportTimespan;
|
||||
class ExportChannelConfiguration;
|
||||
class ExportFormatSpecification;
|
||||
class ExportFilename;
|
||||
class ExportProcessor;
|
||||
|
||||
class ExportElementFactory
|
||||
{
|
||||
protected:
|
||||
typedef boost::shared_ptr<ExportTimespan> TimespanPtr;
|
||||
typedef boost::shared_ptr<ExportChannelConfiguration> ChannelConfigPtr;
|
||||
typedef boost::shared_ptr<ExportFormatSpecification> FormatPtr;
|
||||
typedef boost::shared_ptr<ExportFilename> FilenamePtr;
|
||||
|
||||
public:
|
||||
|
||||
ExportElementFactory (Session & session);
|
||||
~ExportElementFactory ();
|
||||
|
||||
TimespanPtr add_timespan ();
|
||||
|
||||
ChannelConfigPtr add_channel_config ();
|
||||
|
||||
FormatPtr add_format ();
|
||||
FormatPtr add_format (XMLNode const & state);
|
||||
FormatPtr add_format_copy (FormatPtr other);
|
||||
|
||||
FilenamePtr add_filename ();
|
||||
FilenamePtr add_filename_copy (FilenamePtr other);
|
||||
|
||||
private:
|
||||
Session & session;
|
||||
};
|
||||
|
||||
class ExportHandler : public ExportElementFactory, public sigc::trackable
|
||||
{
|
||||
private:
|
||||
|
||||
/* Stuff for export configs
|
||||
* The multimap maps timespans to file specifications
|
||||
*/
|
||||
|
||||
struct FileSpec {
|
||||
|
||||
FileSpec (ChannelConfigPtr channel_config, FormatPtr format, FilenamePtr filename) :
|
||||
channel_config (channel_config),
|
||||
format (format),
|
||||
filename (filename)
|
||||
{}
|
||||
|
||||
ChannelConfigPtr channel_config;
|
||||
FormatPtr format;
|
||||
FilenamePtr filename;
|
||||
};
|
||||
|
||||
typedef std::pair<TimespanPtr, FileSpec> ConfigPair;
|
||||
typedef std::multimap<TimespanPtr, FileSpec> ConfigMap;
|
||||
|
||||
typedef boost::shared_ptr<ExportProcessor> ProcessorPtr;
|
||||
|
||||
private:
|
||||
/* Session::get_export_handler() should be used to obtain an export handler
|
||||
* This ensures that it doesn't go out of scope before finalize_audio_export is called
|
||||
*/
|
||||
|
||||
friend boost::shared_ptr<ExportHandler> Session::get_export_handler();
|
||||
ExportHandler (Session & session);
|
||||
|
||||
public:
|
||||
~ExportHandler ();
|
||||
|
||||
bool add_export_config (TimespanPtr timespan, ChannelConfigPtr channel_config, FormatPtr format, FilenamePtr filename);
|
||||
void do_export (bool rt = false);
|
||||
|
||||
private:
|
||||
|
||||
Session & session;
|
||||
ProcessorPtr processor;
|
||||
ConfigMap config_map;
|
||||
|
||||
bool realtime;
|
||||
|
||||
sigc::connection files_written_connection;
|
||||
std::list<Glib::ustring> files_written;
|
||||
|
||||
/* CD Marker stuff */
|
||||
|
||||
struct CDMarkerStatus {
|
||||
CDMarkerStatus (string out_file, TimespanPtr timespan, FormatPtr format, string filename) :
|
||||
out (out_file.c_str()), timespan (timespan), format (format), filename (filename),
|
||||
track_number (1), track_position (0), track_duration (0), track_start_frame (0),
|
||||
index_number (1), index_position (0)
|
||||
{}
|
||||
|
||||
/* General info */
|
||||
std::ofstream out;
|
||||
TimespanPtr timespan;
|
||||
FormatPtr format;
|
||||
string filename;
|
||||
Location * marker;
|
||||
|
||||
/* Track info */
|
||||
uint32_t track_number;
|
||||
nframes_t track_position;
|
||||
nframes_t track_duration;
|
||||
nframes_t track_start_frame;
|
||||
|
||||
/* Index info */
|
||||
uint32_t index_number;
|
||||
nframes_t index_position;
|
||||
};
|
||||
|
||||
|
||||
void export_cd_marker_file (TimespanPtr timespan, FormatPtr file_format, std::string filename, CDMarkerFormat format);
|
||||
|
||||
void write_cue_header (CDMarkerStatus & status);
|
||||
void write_toc_header (CDMarkerStatus & status);
|
||||
|
||||
void write_track_info_cue (CDMarkerStatus & status);
|
||||
void write_track_info_toc (CDMarkerStatus & status);
|
||||
|
||||
void write_index_info_cue (CDMarkerStatus & status);
|
||||
void write_index_info_toc (CDMarkerStatus & status);
|
||||
|
||||
void frames_to_cd_frames_string (char* buf, nframes_t when);
|
||||
|
||||
int cue_tracknum;
|
||||
int cue_indexnum;
|
||||
|
||||
/* Timespan management */
|
||||
|
||||
void start_timespan ();
|
||||
void finish_timespan ();
|
||||
void timespan_thread_finished ();
|
||||
|
||||
typedef std::pair<ConfigMap::iterator, ConfigMap::iterator> TimespanBounds;
|
||||
TimespanPtr current_timespan;
|
||||
ConfigMap::iterator current_map_it;
|
||||
TimespanBounds timespan_bounds;
|
||||
sigc::connection channel_config_connection;
|
||||
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_handler_h__ */
|
||||
189
libs/ardour/ardour/export_multiplication.h
Normal file
189
libs/ardour/ardour/export_multiplication.h
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/* This file is not used at the moment. It includes code related to export a
|
||||
* multiplication graph system that can be used together with the ExportMultiplicator
|
||||
* class in the gtk2_ardour folder.
|
||||
* - Sakari Bergen 6.8.2008 -
|
||||
*/
|
||||
|
||||
/*** Graph classes ***/
|
||||
public:
|
||||
|
||||
/// A node in the hierarchical graph that represents a multiplicatable export item
|
||||
class GraphNode {
|
||||
public:
|
||||
GraphNode ();
|
||||
virtual ~GraphNode ();
|
||||
|
||||
uint32_t id() const { return _id; }
|
||||
|
||||
/* Children and parents. Note: only children are kept in order! */
|
||||
|
||||
list<GraphNode *> const & get_parents () const { return parents; }
|
||||
|
||||
void add_child (GraphNode * child, GraphNode * left_sibling);
|
||||
void remove_child (GraphNode * child);
|
||||
GraphNode * first_child () const { return children.front(); }
|
||||
GraphNode * last_child () const { return children.back(); }
|
||||
list<GraphNode *> const & get_children () const { return children; }
|
||||
|
||||
/* Relation functions */
|
||||
|
||||
bool is_ancestor_of (GraphNode const * node) const;
|
||||
bool is_descendant_of (GraphNode const * node) const;
|
||||
bool equals (GraphNode const * node) const { return node == this; }
|
||||
|
||||
/* Selection functions */
|
||||
|
||||
bool selected () const { return _selected; }
|
||||
void select (bool value);
|
||||
|
||||
sigc::signal<void, bool> SelectChanged;
|
||||
|
||||
protected:
|
||||
|
||||
/* Parent manipulation functions should be used only from child manipulation functions! */
|
||||
|
||||
void add_parent (GraphNode * parent);
|
||||
void remove_parent (GraphNode * parent);
|
||||
|
||||
list<GraphNode *> parents;
|
||||
list<GraphNode *> children;
|
||||
|
||||
bool _selected;
|
||||
uint32_t _id;
|
||||
static uint32_t id_counter;
|
||||
};
|
||||
|
||||
/// A graph node that contains data
|
||||
template <typename T>
|
||||
class DataNode : public GraphNode {
|
||||
private:
|
||||
typedef boost::shared_ptr<T> DataPtr;
|
||||
typedef boost::shared_ptr<DataNode<T> > SelfPtr;
|
||||
typedef boost::weak_ptr<DataNode<T> > WeakSelfPtr;
|
||||
|
||||
DataNode (DataPtr data) : _data (data) {}
|
||||
void set_self_ptr (boost::shared_ptr<DataNode<T> > ptr) { _self_ptr = ptr; }
|
||||
|
||||
public:
|
||||
static SelfPtr create (T * data)
|
||||
{
|
||||
SelfPtr ptr = SelfPtr (new DataNode<T> (DataPtr (data)));
|
||||
ptr->set_self_ptr (ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static SelfPtr create (DataPtr data)
|
||||
{
|
||||
SelfPtr ptr = SelfPtr (new DataNode<T> (data));
|
||||
ptr->set_self_ptr (ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
DataPtr data() { return _data; }
|
||||
SelfPtr self_ptr () { return _self_ptr.lock(); }
|
||||
|
||||
template<typename P> // Parent's data type
|
||||
void sort_parents (list<boost::shared_ptr<DataNode<P> > > const & sort_list)
|
||||
{
|
||||
parents.sort (NodeSorter<P> (sort_list));
|
||||
}
|
||||
|
||||
private:
|
||||
DataPtr _data;
|
||||
WeakSelfPtr _self_ptr;
|
||||
};
|
||||
|
||||
private:
|
||||
/* Sorts GraphNodes according to a list of DataNodes */
|
||||
|
||||
template<typename T>
|
||||
class NodeSorter {
|
||||
public:
|
||||
typedef list<boost::shared_ptr<DataNode<T> > > ListType;
|
||||
|
||||
NodeSorter (ListType const & list) : list (list) {}
|
||||
|
||||
bool operator() (GraphNode * one, GraphNode * other) // '<' operator
|
||||
{
|
||||
if (one == other) { return false; } // Strict weak ordering
|
||||
for (typename ListType::const_iterator it = list.begin(); it != list.end(); ++it) {
|
||||
if (it->get() == one) {
|
||||
return true;
|
||||
}
|
||||
if (it->get() == other) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "Invalid comparison list given to NodeSorter" << std::endl;
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
private:
|
||||
ListType const & list;
|
||||
};
|
||||
|
||||
/*** Multiplication management ***/
|
||||
public:
|
||||
|
||||
typedef DataNode<TimespanState> TimespanNode;
|
||||
typedef boost::shared_ptr<TimespanNode> TimespanNodePtr;
|
||||
|
||||
typedef DataNode<ChannelConfigState> ChannelConfigNode;
|
||||
typedef boost::shared_ptr<ChannelConfigNode> ChannelConfigNodePtr;
|
||||
|
||||
typedef DataNode<FormatState> FormatNode;
|
||||
typedef boost::shared_ptr<FormatNode> FormatNodePtr;
|
||||
|
||||
typedef DataNode<FilenameState> FilenameNode;
|
||||
typedef boost::shared_ptr<FilenameNode> FilenameNodePtr;
|
||||
|
||||
struct MultiplicationGraph {
|
||||
list<TimespanNodePtr> timespans;
|
||||
list<ChannelConfigNodePtr> channel_configs;
|
||||
list<FormatNodePtr> formats;
|
||||
list<FilenameNodePtr> filenames;
|
||||
};
|
||||
|
||||
MultiplicationGraph const & get_graph () { return graph; }
|
||||
|
||||
void split_node (GraphNode * node, float position);
|
||||
void remove_node (GraphNode * node);
|
||||
|
||||
sigc::signal<void> GraphChanged;
|
||||
|
||||
private:
|
||||
|
||||
void purge_graph ();
|
||||
|
||||
template<typename T>
|
||||
static void insert_after (list<T> & the_list, T const & position, T const & element);
|
||||
|
||||
template<typename T>
|
||||
static void remove_by_element (list<T> & the_list, T const & element);
|
||||
|
||||
bool nodes_have_one_common_child (list<GraphNode *> const & the_list);
|
||||
list<GraphNode *>::const_iterator end_of_common_child_range (list<GraphNode *> const & the_list, list<GraphNode *>::const_iterator beginning);
|
||||
void split_node_at_position (GraphNode * old_node, GraphNode * new_node, float position);
|
||||
|
||||
void split_timespan (TimespanNodePtr node, float position = 0.5);
|
||||
void split_channel_config (ChannelConfigNodePtr node, float position = 0.5);
|
||||
void split_format (FormatNodePtr node, float position = 0.5);
|
||||
void split_filename (FilenameNodePtr node, float position = 0.5);
|
||||
|
||||
void duplicate_timespan_children (TimespanNodePtr source, TimespanNodePtr target, GraphNode * insertion_point = 0);
|
||||
void duplicate_channel_config_children (ChannelConfigNodePtr source, ChannelConfigNodePtr target, GraphNode * insertion_point = 0);
|
||||
void duplicate_format_children (FormatNodePtr source, FormatNodePtr target, GraphNode * insertion_point = 0);
|
||||
|
||||
TimespanNodePtr duplicate_timespan_node (TimespanNodePtr node);
|
||||
ChannelConfigNodePtr duplicate_channel_config_node (ChannelConfigNodePtr node);
|
||||
FormatNodePtr duplicate_format_node (FormatNodePtr node);
|
||||
FilenameNodePtr duplicate_filename_node (FilenameNodePtr node);
|
||||
|
||||
void remove_timespan (TimespanNodePtr node);
|
||||
void remove_channel_config (ChannelConfigNodePtr node);
|
||||
void remove_format (FormatNodePtr node);
|
||||
void remove_filename (FilenameNodePtr node);
|
||||
|
||||
MultiplicationGraph graph;
|
||||
139
libs/ardour/ardour/export_processor.h
Normal file
139
libs/ardour/ardour/export_processor.h
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_processor_h__
|
||||
#define __ardour_export_processor_h__
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/smart_ptr.hpp>
|
||||
#include <glibmm/ustring.h>
|
||||
|
||||
#include <ardour/graph.h>
|
||||
#include <ardour/export_file_io.h>
|
||||
#include <ardour/export_utilities.h>
|
||||
|
||||
using Glib::ustring;
|
||||
using std::list;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class Session;
|
||||
class ExportStatus;
|
||||
class ExportFilename;
|
||||
class ExportFormatSpecification;
|
||||
|
||||
/// Sets up components for export post processing
|
||||
class ExportProcessor
|
||||
{
|
||||
private:
|
||||
/* Typedefs for utility processors */
|
||||
|
||||
typedef boost::shared_ptr<SampleRateConverter> SRConverterPtr;
|
||||
typedef boost::shared_ptr<PeakReader> PReaderPtr;
|
||||
typedef boost::shared_ptr<Normalizer> NormalizerPtr;
|
||||
typedef boost::shared_ptr<ExportTempFile> TempFilePtr;
|
||||
|
||||
typedef boost::shared_ptr<SampleFormatConverter<short> > ShortConverterPtr;
|
||||
typedef boost::shared_ptr<SampleFormatConverter<int> > IntConverterPtr;
|
||||
typedef boost::shared_ptr<SampleFormatConverter<float> > FloatConverterPtr;
|
||||
|
||||
typedef boost::shared_ptr<SndfileWriter<short> > ShortWriterPtr;
|
||||
typedef boost::shared_ptr<SndfileWriter<int> > IntWriterPtr;
|
||||
typedef boost::shared_ptr<SndfileWriter<float> > FloatWriterPtr;
|
||||
|
||||
typedef GraphSink<float> FloatSink;
|
||||
typedef boost::shared_ptr<FloatSink> FloatSinkPtr;
|
||||
typedef std::vector<FloatSinkPtr> FloatSinkVect;
|
||||
|
||||
typedef boost::shared_ptr<ExportFilename> FilenamePtr;
|
||||
typedef boost::shared_ptr<ExportFormatSpecification const> FormatPtr;
|
||||
|
||||
typedef boost::shared_ptr<ExportFileWriter> FileWriterPtr;
|
||||
typedef std::list<FileWriterPtr> FileWriterList;
|
||||
|
||||
public:
|
||||
|
||||
ExportProcessor (Session & session);
|
||||
~ExportProcessor ();
|
||||
ExportProcessor * copy() { return new ExportProcessor (session); }
|
||||
|
||||
/// Do preparations for exporting
|
||||
/** Should be called before process
|
||||
* @return 0 on success
|
||||
*/
|
||||
int prepare (FormatPtr format, FilenamePtr fname, uint32_t chans, bool split = false, nframes_t start = 0);
|
||||
|
||||
/// Process data
|
||||
/** @param frames frames to process @return frames written **/
|
||||
nframes_t process (float * data, nframes_t frames);
|
||||
|
||||
/** should be called after all data is given to process **/
|
||||
void prepare_post_processors ();
|
||||
|
||||
void write_files ();
|
||||
|
||||
static sigc::signal<void, Glib::ustring> WritingFile;
|
||||
|
||||
private:
|
||||
|
||||
void reset ();
|
||||
FloatSinkPtr prepare_sndfile_writer (FormatPtr format, uint32_t channels, ustring const & filename);
|
||||
|
||||
Session & session;
|
||||
ExportStatus & status;
|
||||
|
||||
/* these are initalized in prepare() */
|
||||
|
||||
FilenamePtr filename;
|
||||
NormalizerPtr normalizer;
|
||||
SRConverterPtr src;
|
||||
PReaderPtr peak_reader;
|
||||
TempFilePtr temp_file;
|
||||
FloatSinkVect file_sinks;
|
||||
FileWriterList writer_list;
|
||||
|
||||
/* general info */
|
||||
|
||||
uint32_t channels;
|
||||
nframes_t blocksize;
|
||||
nframes_t frame_rate;
|
||||
|
||||
/* Processing */
|
||||
|
||||
bool tag;
|
||||
bool broadcast_info;
|
||||
bool split_files;
|
||||
bool normalize;
|
||||
bool trim_beginning;
|
||||
bool trim_end;
|
||||
nframes_t silence_beginning;
|
||||
nframes_t silence_end;
|
||||
|
||||
/* Progress info */
|
||||
|
||||
nframes_t temp_file_position;
|
||||
nframes_t temp_file_length;
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_processor_h__ */
|
||||
316
libs/ardour/ardour/export_profile_manager.h
Normal file
316
libs/ardour/ardour/export_profile_manager.h
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_profile_manager_h__
|
||||
#define __ardour_export_profile_manager_h__
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/weak_ptr.hpp>
|
||||
#include <sigc++/signal.h>
|
||||
#include <glibmm/ustring.h>
|
||||
|
||||
#include <pbd/file_utils.h>
|
||||
#include <pbd/xml++.h>
|
||||
|
||||
#include <ardour/filesystem_paths.h>
|
||||
#include <ardour/location.h>
|
||||
#include <ardour/types.h>
|
||||
|
||||
using std::string;
|
||||
using std::list;
|
||||
using std::set;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class ExportHandler;
|
||||
class ExportTimespan;
|
||||
class ExportChannelConfiguration;
|
||||
class ExportFormatSpecification;
|
||||
class ExportFilename;
|
||||
class Location;
|
||||
class Session;
|
||||
|
||||
/// Manages (de)serialization of export profiles and related classes
|
||||
class ExportProfileManager
|
||||
{
|
||||
public:
|
||||
class Preset {
|
||||
public:
|
||||
Preset (string filename, Session & s);
|
||||
~Preset ();
|
||||
|
||||
uint32_t id () const { return _id; }
|
||||
string name () const { return _name; }
|
||||
|
||||
void set_name (string name);
|
||||
void set_id (uint32_t id);
|
||||
|
||||
// Note: The set_..._state functions take ownership of the XMLNode
|
||||
void set_global_state (XMLNode & state);
|
||||
void set_local_state (XMLNode & state);
|
||||
|
||||
XMLNode const * get_global_state () const { return global.root(); }
|
||||
XMLNode const * get_local_state () const { return local; }
|
||||
|
||||
void save () const;
|
||||
void remove_local () const;
|
||||
|
||||
private:
|
||||
|
||||
XMLNode * get_instant_xml () const;
|
||||
void save_instant_xml () const;
|
||||
void remove_instant_xml () const;
|
||||
|
||||
uint32_t _id;
|
||||
string _name;
|
||||
|
||||
Session & session;
|
||||
XMLTree global;
|
||||
XMLNode * local;
|
||||
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<Preset> PresetPtr;
|
||||
typedef std::list<PresetPtr> PresetList;
|
||||
|
||||
public:
|
||||
|
||||
ExportProfileManager (Session & s);
|
||||
~ExportProfileManager ();
|
||||
|
||||
void load_profile ();
|
||||
void prepare_for_export ();
|
||||
|
||||
PresetList const & get_presets () { return preset_list; }
|
||||
void load_preset (PresetPtr preset);
|
||||
PresetPtr save_preset (string const & name);
|
||||
void remove_preset ();
|
||||
|
||||
private:
|
||||
typedef boost::shared_ptr<ExportHandler> HandlerPtr;
|
||||
|
||||
typedef std::pair<uint32_t, PBD::sys::path> FilePair;
|
||||
typedef std::map<uint32_t, PBD::sys::path> FileMap;
|
||||
|
||||
HandlerPtr handler;
|
||||
Session & session;
|
||||
|
||||
void load_presets ();
|
||||
uint32_t load_preset_from_disk (PBD::sys::path const & path); // Returns preset id
|
||||
|
||||
void set_state (XMLNode const & root);
|
||||
void set_global_state (XMLNode const & root);
|
||||
void set_local_state (XMLNode const & root);
|
||||
|
||||
void serialize_profile (XMLNode & root);
|
||||
void serialize_global_profile (XMLNode & root);
|
||||
void serialize_local_profile (XMLNode & root);
|
||||
|
||||
PresetList preset_list;
|
||||
PresetPtr current_preset;
|
||||
uint32_t preset_id_counter;
|
||||
FileMap preset_file_map;
|
||||
|
||||
std::vector<PBD::sys::path> find_file (std::string const & pattern);
|
||||
|
||||
PBD::sys::path export_config_dir;
|
||||
PBD::SearchPath search_path;
|
||||
|
||||
/* Timespans */
|
||||
public:
|
||||
|
||||
typedef boost::shared_ptr<ExportTimespan> TimespanPtr;
|
||||
typedef std::list<TimespanPtr> TimespanList;
|
||||
typedef boost::shared_ptr<TimespanList> TimespanListPtr;
|
||||
typedef std::list<Location *> LocationList;
|
||||
|
||||
enum TimeFormat {
|
||||
SMPTE,
|
||||
BBT,
|
||||
MinSec,
|
||||
Frames,
|
||||
Off
|
||||
};
|
||||
|
||||
struct TimespanState {
|
||||
TimespanListPtr timespans;
|
||||
TimeFormat time_format;
|
||||
|
||||
boost::shared_ptr<Location> session_range;
|
||||
boost::shared_ptr<Location> selection_range;
|
||||
boost::shared_ptr<LocationList> ranges;
|
||||
|
||||
TimespanState (boost::shared_ptr<Location> session_range,
|
||||
boost::shared_ptr<Location> selection_range,
|
||||
boost::shared_ptr<LocationList> ranges) :
|
||||
timespans (new TimespanList ()),
|
||||
time_format (SMPTE),
|
||||
|
||||
session_range (session_range),
|
||||
selection_range (selection_range),
|
||||
ranges (ranges)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<TimespanState> TimespanStatePtr;
|
||||
typedef std::list<TimespanStatePtr> TimespanStateList;
|
||||
|
||||
void set_selection_range (nframes_t start = 0, nframes_t end = 0);
|
||||
TimespanStateList const & get_timespans () { return timespans; }
|
||||
|
||||
private:
|
||||
|
||||
TimespanStateList timespans;
|
||||
|
||||
void init_timespans (XMLNodeList nodes);
|
||||
|
||||
TimespanStatePtr deserialize_timespan (XMLNode & root);
|
||||
XMLNode & serialize_timespan (TimespanStatePtr state);
|
||||
|
||||
/* Locations */
|
||||
|
||||
void update_ranges ();
|
||||
|
||||
boost::shared_ptr<Location> session_range;
|
||||
boost::shared_ptr<Location> selection_range;
|
||||
boost::shared_ptr<LocationList> ranges;
|
||||
|
||||
/* Channel Configs */
|
||||
public:
|
||||
|
||||
typedef boost::shared_ptr<ExportChannelConfiguration> ChannelConfigPtr;
|
||||
|
||||
struct ChannelConfigState {
|
||||
ChannelConfigPtr config;
|
||||
|
||||
ChannelConfigState (ChannelConfigPtr ptr) : config (ptr) {}
|
||||
};
|
||||
typedef boost::shared_ptr<ChannelConfigState> ChannelConfigStatePtr;
|
||||
typedef std::list<ChannelConfigStatePtr> ChannelConfigStateList;
|
||||
|
||||
ChannelConfigStateList const & get_channel_configs () { return channel_configs; }
|
||||
|
||||
private:
|
||||
|
||||
ChannelConfigStateList channel_configs;
|
||||
|
||||
void init_channel_configs (XMLNodeList nodes);
|
||||
|
||||
ChannelConfigStatePtr deserialize_channel_config (XMLNode & root);
|
||||
XMLNode & serialize_channel_config (ChannelConfigStatePtr state);
|
||||
|
||||
/* Formats */
|
||||
public:
|
||||
|
||||
typedef boost::shared_ptr<ExportFormatSpecification> FormatPtr;
|
||||
typedef std::list<FormatPtr> FormatList;
|
||||
|
||||
struct FormatState {
|
||||
boost::shared_ptr<FormatList const> list;
|
||||
FormatPtr format;
|
||||
|
||||
FormatState (boost::shared_ptr<FormatList const> list, FormatPtr format) :
|
||||
list (list), format (format) {}
|
||||
};
|
||||
typedef boost::shared_ptr<FormatState> FormatStatePtr;
|
||||
typedef std::list<FormatStatePtr> FormatStateList;
|
||||
|
||||
FormatStateList const & get_formats () { return formats; }
|
||||
FormatStatePtr duplicate_format_state (FormatStatePtr state);
|
||||
void remove_format_state (FormatStatePtr state);
|
||||
|
||||
PBD::sys::path save_format_to_disk (FormatPtr format);
|
||||
void remove_format_profile (FormatPtr format);
|
||||
FormatPtr get_new_format (FormatPtr original);
|
||||
|
||||
sigc::signal<void> FormatListChanged;
|
||||
|
||||
private:
|
||||
|
||||
FormatStateList formats;
|
||||
|
||||
void init_formats (XMLNodeList nodes);
|
||||
FormatStatePtr deserialize_format (XMLNode & root);
|
||||
XMLNode & serialize_format (FormatStatePtr state);
|
||||
|
||||
void load_formats ();
|
||||
|
||||
FormatPtr load_format (XMLNode & node);
|
||||
void load_format_from_disk (PBD::sys::path const & path);
|
||||
|
||||
boost::shared_ptr<FormatList> format_list;
|
||||
FileMap format_file_map;
|
||||
|
||||
/* Filenames */
|
||||
public:
|
||||
|
||||
typedef boost::shared_ptr<ExportFilename> FilenamePtr;
|
||||
|
||||
struct FilenameState {
|
||||
FilenamePtr filename;
|
||||
|
||||
FilenameState (FilenamePtr ptr) : filename (ptr) {}
|
||||
};
|
||||
typedef boost::shared_ptr<FilenameState> FilenameStatePtr;
|
||||
typedef std::list<FilenameStatePtr> FilenameStateList;
|
||||
|
||||
FilenameStateList const & get_filenames () { return filenames; }
|
||||
FilenameStatePtr duplicate_filename_state (FilenameStatePtr state);
|
||||
void remove_filename_state (FilenameStatePtr state);
|
||||
|
||||
private:
|
||||
|
||||
FilenameStateList filenames;
|
||||
|
||||
void init_filenames (XMLNodeList nodes);
|
||||
|
||||
FilenameStatePtr deserialize_filename (XMLNode & root);
|
||||
XMLNode & serialize_filename (FilenameStatePtr state);
|
||||
|
||||
FilenamePtr load_filename (XMLNode & node);
|
||||
|
||||
/* Warnings */
|
||||
public:
|
||||
struct Warnings {
|
||||
std::list<Glib::ustring> errors;
|
||||
std::list<Glib::ustring> warnings;
|
||||
std::list<Glib::ustring> conflicting_filenames;
|
||||
};
|
||||
|
||||
boost::shared_ptr<Warnings> get_warnings ();
|
||||
|
||||
private:
|
||||
void check_config (boost::shared_ptr<Warnings> warnings,
|
||||
TimespanStatePtr timespan_state,
|
||||
ChannelConfigStatePtr channel_config_state,
|
||||
FormatStatePtr format_state,
|
||||
FilenameStatePtr filename_state);
|
||||
};
|
||||
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_profile_manager_h__ */
|
||||
72
libs/ardour/ardour/export_status.h
Normal file
72
libs/ardour/ardour/export_status.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_status_h__
|
||||
#define __ardour_export_status_h__
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <sigc++/signal.h>
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
enum ExportStage {
|
||||
export_None,
|
||||
export_ReadTimespan,
|
||||
export_PostProcess,
|
||||
export_Write
|
||||
};
|
||||
|
||||
struct ExportStatus {
|
||||
|
||||
ExportStatus ();
|
||||
void init ();
|
||||
|
||||
/* Status info */
|
||||
|
||||
volatile bool stop;
|
||||
volatile bool running;
|
||||
|
||||
sigc::signal<void> Aborting;
|
||||
void abort () { _aborted = true; Aborting(); }
|
||||
bool aborted () const { return _aborted; }
|
||||
|
||||
/* Progress info */
|
||||
|
||||
volatile ExportStage stage;
|
||||
volatile float progress;
|
||||
|
||||
volatile uint32_t total_timespans;
|
||||
volatile uint32_t timespan;
|
||||
|
||||
volatile uint32_t total_channel_configs;
|
||||
volatile uint32_t channel_config;
|
||||
|
||||
volatile uint32_t total_formats;
|
||||
volatile uint32_t format;
|
||||
|
||||
private:
|
||||
volatile bool _aborted;
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_status_h__ */
|
||||
97
libs/ardour/ardour/export_timespan.h
Normal file
97
libs/ardour/ardour/export_timespan.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_timespan_h__
|
||||
#define __ardour_export_timespan_h__
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
#include <glibmm/ustring.h>
|
||||
|
||||
#include <ardour/export_status.h>
|
||||
#include <ardour/ardour.h>
|
||||
|
||||
using Glib::ustring;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class ExportChannel;
|
||||
class ExportTempFile;
|
||||
|
||||
class ExportTimespan : public sigc::trackable
|
||||
{
|
||||
private:
|
||||
typedef boost::shared_ptr<ExportTempFile> TempFilePtr;
|
||||
typedef std::pair<ExportChannel const, TempFilePtr> ChannelFilePair;
|
||||
typedef std::map<ExportChannel const, TempFilePtr> TempFileMap;
|
||||
|
||||
private:
|
||||
friend class ExportElementFactory;
|
||||
ExportTimespan (ExportStatus & status, nframes_t frame_rate);
|
||||
|
||||
public:
|
||||
~ExportTimespan ();
|
||||
|
||||
ustring name () const { return _name; }
|
||||
void set_name (ustring name) { _name = name; }
|
||||
|
||||
ustring range_id () const { return _range_id; }
|
||||
void set_range_id (ustring range_id) { _range_id = range_id; }
|
||||
|
||||
/// Registers a channel to be read when export starts rolling
|
||||
void register_channel (ExportChannel const & channel);
|
||||
|
||||
/// "Rewinds" the tempfiles to start reading the beginnings again
|
||||
void rewind ();
|
||||
|
||||
/// Reads data from the tempfile belonging to channel into data
|
||||
nframes_t get_data (float * data, nframes_t frames, ExportChannel const & channel);
|
||||
|
||||
/// Reads data from each channel and writes to tempfile
|
||||
int process (nframes_t frames);
|
||||
|
||||
sigc::connection process_connection;
|
||||
|
||||
void set_range (nframes_t start, nframes_t end);
|
||||
nframes_t get_length () const { return end_frame - start_frame; }
|
||||
nframes_t get_start () const { return start_frame; }
|
||||
nframes_t get_end () const { return end_frame; }
|
||||
|
||||
private:
|
||||
|
||||
ExportStatus & status;
|
||||
|
||||
nframes_t start_frame;
|
||||
nframes_t end_frame;
|
||||
nframes_t position;
|
||||
nframes_t frame_rate;
|
||||
|
||||
TempFileMap filemap;
|
||||
|
||||
ustring _name;
|
||||
ustring _range_id;
|
||||
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_timespan_h__ */
|
||||
146
libs/ardour/ardour/export_utilities.h
Normal file
146
libs/ardour/ardour/export_utilities.h
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_export_utilities_h__
|
||||
#define __ardour_export_utilities_h__
|
||||
|
||||
#include <samplerate.h>
|
||||
|
||||
#include <ardour/graph.h>
|
||||
#include <ardour/types.h>
|
||||
#include <ardour/ardour.h>
|
||||
#include <ardour/export_format_base.h>
|
||||
#include <ardour/runtime_functions.h>
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
/* Processors */
|
||||
|
||||
/* Sample rate converter */
|
||||
|
||||
class SampleRateConverter : public GraphSinkVertex<float, float>
|
||||
{
|
||||
public:
|
||||
SampleRateConverter (uint32_t channels, nframes_t in_rate, nframes_t out_rate, int quality);
|
||||
~SampleRateConverter ();
|
||||
|
||||
protected:
|
||||
nframes_t process (float * data, nframes_t frames);
|
||||
|
||||
private:
|
||||
bool active;
|
||||
uint32_t channels;
|
||||
|
||||
nframes_t leftover_frames;
|
||||
nframes_t max_leftover_frames;
|
||||
nframes_t frames_in;
|
||||
nframes_t frames_out;
|
||||
|
||||
float * data_in;
|
||||
float * leftover_data;
|
||||
|
||||
float * data_out;
|
||||
nframes_t data_out_size;
|
||||
|
||||
SRC_DATA src_data;
|
||||
SRC_STATE* src_state;
|
||||
};
|
||||
|
||||
/* Sample format converter */
|
||||
|
||||
template <typename TOut>
|
||||
class SampleFormatConverter : public GraphSinkVertex<float, TOut>
|
||||
{
|
||||
public:
|
||||
SampleFormatConverter (uint32_t channels, ExportFormatBase::DitherType type = ExportFormatBase::D_None, int data_width_ = 0);
|
||||
~SampleFormatConverter ();
|
||||
|
||||
void set_clip_floats (bool yn) { clip_floats = yn; }
|
||||
|
||||
protected:
|
||||
nframes_t process (float * data, nframes_t frames);
|
||||
|
||||
private:
|
||||
uint32_t channels;
|
||||
int data_width;
|
||||
GDither dither;
|
||||
nframes_t data_out_size;
|
||||
TOut * data_out;
|
||||
|
||||
bool clip_floats;
|
||||
|
||||
};
|
||||
|
||||
/* Peak reader */
|
||||
|
||||
class PeakReader : public GraphSinkVertex<float, float>
|
||||
{
|
||||
public:
|
||||
PeakReader (uint32_t channels) : channels (channels), peak (0) {}
|
||||
~PeakReader () {}
|
||||
|
||||
float get_peak () { return peak; }
|
||||
|
||||
protected:
|
||||
nframes_t process (float * data, nframes_t frames)
|
||||
{
|
||||
peak = compute_peak (data, channels * frames, peak);
|
||||
return piped_to->write (data, frames);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t channels;
|
||||
float peak;
|
||||
};
|
||||
|
||||
/* Normalizer */
|
||||
|
||||
class Normalizer : public GraphSinkVertex<float, float>
|
||||
{
|
||||
public:
|
||||
Normalizer (uint32_t channels, float target_dB);
|
||||
~Normalizer ();
|
||||
|
||||
void set_peak (float peak);
|
||||
|
||||
protected:
|
||||
nframes_t process (float * data, nframes_t frames);
|
||||
|
||||
private:
|
||||
uint32_t channels;
|
||||
|
||||
bool enabled;
|
||||
gain_t target;
|
||||
gain_t gain;
|
||||
};
|
||||
|
||||
/* Other */
|
||||
|
||||
class NullSink : public GraphSink<float>
|
||||
{
|
||||
public:
|
||||
nframes_t write (float * data, nframes_t frames) { return frames; }
|
||||
};
|
||||
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_export_utilities_h__ */
|
||||
101
libs/ardour/ardour/graph.h
Normal file
101
libs/ardour/ardour/graph.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_graph_h__
|
||||
#define __ardour_graph_h__
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <ardour/types.h>
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
/// Takes data in
|
||||
template <typename T>
|
||||
class GraphSink {
|
||||
public:
|
||||
GraphSink () : end_of_input (false) {}
|
||||
virtual ~GraphSink () { end_of_input = false; }
|
||||
|
||||
// writes data and return number of frames written
|
||||
virtual nframes_t write (T * data, nframes_t frames) = 0;
|
||||
|
||||
// Notifies end of input. All left over data must be written at this stage
|
||||
virtual void set_end_of_input (bool state = true)
|
||||
{
|
||||
end_of_input = state;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool end_of_input;
|
||||
};
|
||||
|
||||
/// is a source for data
|
||||
template <typename T>
|
||||
class GraphSource {
|
||||
public:
|
||||
GraphSource () {}
|
||||
virtual ~GraphSource () {}
|
||||
|
||||
virtual nframes_t read (T * data, nframes_t frames) = 0;
|
||||
};
|
||||
|
||||
/// Takes data in, processes it and passes it on to another sink
|
||||
template <typename TIn, typename TOut>
|
||||
class GraphSinkVertex : public GraphSink<TIn> {
|
||||
public:
|
||||
GraphSinkVertex () {}
|
||||
virtual ~GraphSinkVertex () {}
|
||||
|
||||
void pipe_to (boost::shared_ptr<GraphSink<TOut> > dest) {
|
||||
piped_to = dest;
|
||||
}
|
||||
|
||||
nframes_t write (TIn * data, nframes_t frames)
|
||||
{
|
||||
if (!piped_to) {
|
||||
return -1;
|
||||
}
|
||||
return process (data, frames);
|
||||
}
|
||||
|
||||
virtual void set_end_of_input (bool state = true)
|
||||
{
|
||||
if (!piped_to) {
|
||||
return;
|
||||
}
|
||||
piped_to->set_end_of_input (state);
|
||||
GraphSink<TIn>::end_of_input = state;
|
||||
}
|
||||
|
||||
protected:
|
||||
boost::shared_ptr<GraphSink<TOut> > piped_to;
|
||||
|
||||
/* process must process data,
|
||||
use piped_to->write to write the data
|
||||
and return number of frames written */
|
||||
virtual nframes_t process (TIn * data, nframes_t frames) = 0;
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_graph_h__ */
|
||||
|
||||
132
libs/ardour/ardour/session_metadata.h
Normal file
132
libs/ardour/ardour/session_metadata.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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_session_metadata_h__
|
||||
#define __ardour_session_metadata_h__
|
||||
|
||||
#include <string>
|
||||
#include <glibmm/ustring.h>
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#include <pbd/statefuldestructible.h>
|
||||
#include <pbd/xml++.h>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
using std::string;
|
||||
using Glib::ustring;
|
||||
|
||||
/** Represents metadata associated to a Session
|
||||
* Metadata can be accessed and edited via this class.
|
||||
* Exported files can also be tagged with this data.
|
||||
*/
|
||||
class SessionMetadata : public PBD::StatefulDestructible
|
||||
{
|
||||
public:
|
||||
SessionMetadata ();
|
||||
~SessionMetadata ();
|
||||
|
||||
/*** Accessing ***/
|
||||
ustring comment () const;
|
||||
ustring copyright () const;
|
||||
ustring isrc () const;
|
||||
uint32_t year () const;
|
||||
|
||||
ustring grouping () const;
|
||||
ustring title () const;
|
||||
ustring subtitle () const;
|
||||
|
||||
ustring artist () const;
|
||||
ustring album_artist () const;
|
||||
ustring lyricist () const;
|
||||
ustring composer () const;
|
||||
ustring conductor () const;
|
||||
ustring remixer () const;
|
||||
ustring arranger () const;
|
||||
ustring engineer () const;
|
||||
ustring producer () const;
|
||||
ustring dj_mixer () const;
|
||||
ustring mixer () const;
|
||||
|
||||
ustring album () const;
|
||||
ustring compilation () const;
|
||||
ustring disc_subtitle () const;
|
||||
uint32_t disc_number () const;
|
||||
uint32_t total_discs () const;
|
||||
uint32_t track_number () const;
|
||||
uint32_t total_tracks () const;
|
||||
|
||||
ustring genre () const;
|
||||
|
||||
/*** Editing ***/
|
||||
void set_comment (const ustring &);
|
||||
void set_copyright (const ustring &);
|
||||
void set_isrc (const ustring &);
|
||||
void set_year (uint32_t);
|
||||
|
||||
void set_grouping (const ustring &);
|
||||
void set_title (const ustring &);
|
||||
void set_subtitle (const ustring &);
|
||||
|
||||
void set_artist (const ustring &);
|
||||
void set_album_artist (const ustring &);
|
||||
void set_lyricist (const ustring &);
|
||||
void set_composer (const ustring &);
|
||||
void set_conductor (const ustring &);
|
||||
void set_remixer (const ustring &);
|
||||
void set_arranger (const ustring &);
|
||||
void set_engineer (const ustring &);
|
||||
void set_producer (const ustring &);
|
||||
void set_dj_mixer (const ustring &);
|
||||
void set_mixer (const ustring &);
|
||||
|
||||
void set_album (const ustring &);
|
||||
void set_compilation (const ustring &);
|
||||
void set_disc_subtitle (const ustring &);
|
||||
void set_disc_number (uint32_t);
|
||||
void set_total_discs (uint32_t);
|
||||
void set_track_number (uint32_t);
|
||||
void set_total_tracks (uint32_t);
|
||||
|
||||
void set_genre (const ustring &);
|
||||
|
||||
/*** Serialization ***/
|
||||
XMLNode & get_state ();
|
||||
int set_state (const XMLNode &);
|
||||
|
||||
private:
|
||||
|
||||
typedef std::pair<ustring, ustring> Property;
|
||||
typedef std::map<ustring, ustring> PropertyMap;
|
||||
PropertyMap map;
|
||||
|
||||
XMLNode * get_xml (const ustring & name);
|
||||
|
||||
ustring get_value (const ustring & name) const;
|
||||
uint32_t get_uint_value (const ustring & name) const;
|
||||
|
||||
void set_value (const ustring & name, const ustring & value);
|
||||
void set_value (const ustring & name, uint32_t value);
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif // __ardour_session_metadata_h__
|
||||
117
libs/ardour/audiofile_tagger.cc
Normal file
117
libs/ardour/audiofile_tagger.cc
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/audiofile_tagger.h>
|
||||
|
||||
#include <ardour/session_metadata.h>
|
||||
|
||||
#include <pbd/convert.h>
|
||||
|
||||
#include <taglib/fileref.h>
|
||||
#include <taglib/oggfile.h>
|
||||
#include <taglib/flacfile.h>
|
||||
|
||||
/* Convert Glib::ustring to TagLib::String */
|
||||
#define TL_STR(ustring) TagLib::String ((ustring).c_str(), TagLib::String::UTF8)
|
||||
|
||||
using namespace PBD;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
bool
|
||||
AudiofileTagger::tag_file (string const & filename, SessionMetadata const & metadata)
|
||||
{
|
||||
TagLib::FileRef file (filename.c_str());
|
||||
TagLib::Tag & tag (*file.tag());
|
||||
|
||||
tag_generic (tag, metadata);
|
||||
|
||||
/* FLAC */
|
||||
|
||||
TagLib::FLAC::File * flac_file;
|
||||
if ((flac_file = dynamic_cast<TagLib::FLAC::File *> (file.file()))) {
|
||||
TagLib::Ogg::XiphComment * vorbis_tag;
|
||||
if ((vorbis_tag = dynamic_cast<TagLib::Ogg::XiphComment *> (flac_file->xiphComment (true)))) {
|
||||
tag_vorbis_comment (*vorbis_tag, metadata);
|
||||
} else {
|
||||
std::cerr << "Could not get Xiph comment for FLAC file!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ogg */
|
||||
|
||||
TagLib::Ogg::File * ogg_file;
|
||||
if ((ogg_file = dynamic_cast<TagLib::Ogg::File *> (file.file()))) {
|
||||
TagLib::Ogg::XiphComment * vorbis_tag;
|
||||
if ((vorbis_tag = dynamic_cast<TagLib::Ogg::XiphComment *> (ogg_file->tag()))) {
|
||||
tag_vorbis_comment (*vorbis_tag, metadata);
|
||||
} else {
|
||||
std::cerr << "Could not get Xiph comment for Ogg file!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
file.save();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AudiofileTagger::tag_generic (TagLib::Tag & tag, SessionMetadata const & metadata)
|
||||
{
|
||||
tag.setTitle (TL_STR(metadata.title()));
|
||||
tag.setArtist (TL_STR(metadata.artist()));
|
||||
tag.setAlbum (TL_STR(metadata.album()));
|
||||
tag.setComment (TL_STR(metadata.comment()));
|
||||
tag.setGenre (TL_STR(metadata.genre()));
|
||||
tag.setYear (metadata.year());
|
||||
tag.setTrack (metadata.track_number());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AudiofileTagger::tag_vorbis_comment (TagLib::Ogg::XiphComment & tag, SessionMetadata const & metadata)
|
||||
{
|
||||
tag.addField ("COPYRIGHT", TL_STR(metadata.copyright()));
|
||||
tag.addField ("ISRC", TL_STR(metadata.isrc()));
|
||||
tag.addField ("GROUPING ", TL_STR(metadata.grouping()));
|
||||
tag.addField ("SUBTITLE", TL_STR(metadata.subtitle()));
|
||||
tag.addField ("ALBUMARTIST", TL_STR(metadata.album_artist()));
|
||||
tag.addField ("LYRICIST", TL_STR(metadata.lyricist()));
|
||||
tag.addField ("COMPOSER", TL_STR(metadata.composer()));
|
||||
tag.addField ("CONDUCTOR", TL_STR(metadata.conductor()));
|
||||
tag.addField ("REMIXER", TL_STR(metadata.remixer()));
|
||||
tag.addField ("ARRANGER", TL_STR(metadata.arranger()));
|
||||
tag.addField ("ENGINEER", TL_STR(metadata.engineer()));
|
||||
tag.addField ("PRODUCER", TL_STR(metadata.producer()));
|
||||
tag.addField ("DJMIXER", TL_STR(metadata.dj_mixer()));
|
||||
tag.addField ("MIXER", TL_STR(metadata.mixer()));
|
||||
tag.addField ("COMPILATION", TL_STR(metadata.compilation()));
|
||||
tag.addField ("DISCSUBTITLE", TL_STR(metadata.disc_subtitle()));
|
||||
tag.addField ("DISCNUMBER", to_string (metadata.disc_number(), std::dec));
|
||||
|
||||
// No field for total discs or tracks
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
268
libs/ardour/broadcast_info.cc
Normal file
268
libs/ardour/broadcast_info.cc
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/broadcast_info.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <glibmm.h>
|
||||
|
||||
#include <ardour/svn_revision.h>
|
||||
#include <ardour/ardour.h>
|
||||
#include <ardour/session.h>
|
||||
|
||||
#include <pbd/convert.h>
|
||||
|
||||
using namespace PBD;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
BroadcastInfo::BroadcastInfo () :
|
||||
_has_info (false)
|
||||
{
|
||||
info = new SF_BROADCAST_INFO;
|
||||
memset (info, 0, sizeof (*info));
|
||||
|
||||
// Note: Set version to 1 when UMID is used, otherwise version should stay at 0
|
||||
info->version = 0;
|
||||
|
||||
time_t rawtime;
|
||||
std::time (&rawtime);
|
||||
_time = *localtime (&rawtime);
|
||||
}
|
||||
|
||||
BroadcastInfo::~BroadcastInfo ()
|
||||
{
|
||||
delete info;
|
||||
}
|
||||
|
||||
void
|
||||
BroadcastInfo::set_from_session (Session const & session, int64_t time)
|
||||
{
|
||||
set_description (session.name());
|
||||
set_time_reference (time);
|
||||
set_origination_time ();
|
||||
set_originator ();
|
||||
set_originator_ref ();
|
||||
}
|
||||
|
||||
bool
|
||||
BroadcastInfo::load_from_file (string const & filename)
|
||||
{
|
||||
SNDFILE * file = 0;
|
||||
SF_INFO info;
|
||||
|
||||
info.format = 0;
|
||||
|
||||
if (!(file = sf_open (filename.c_str(), SFM_READ, &info))) {
|
||||
update_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = load_from_file (file);
|
||||
|
||||
sf_close (file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
BroadcastInfo::load_from_file (SNDFILE* sf)
|
||||
{
|
||||
if (sf_command (sf, SFC_GET_BROADCAST_INFO, info, sizeof (*info)) != SF_TRUE) {
|
||||
update_error();
|
||||
_has_info = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
_has_info = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
string
|
||||
BroadcastInfo::get_description () const
|
||||
{
|
||||
return info->description;
|
||||
}
|
||||
|
||||
int64_t
|
||||
BroadcastInfo::get_time_reference () const
|
||||
{
|
||||
if (!_has_info) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t ret = (uint32_t) info->time_reference_high;
|
||||
ret <<= 32;
|
||||
ret |= (uint32_t) info->time_reference_low;
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct tm
|
||||
BroadcastInfo::get_origination_time () const
|
||||
{
|
||||
struct tm ret;
|
||||
|
||||
string date = info->origination_date;
|
||||
ret.tm_year = atoi (date.substr (0, 4)) - 1900;
|
||||
ret.tm_mon = atoi (date.substr (5, 2));
|
||||
ret.tm_mday = atoi (date.substr (8, 2));
|
||||
|
||||
string time = info->origination_time;
|
||||
ret.tm_hour = atoi (time.substr (0,2));
|
||||
ret.tm_min = atoi (time.substr (3,2));
|
||||
ret.tm_sec = atoi (time.substr (6,2));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
string
|
||||
BroadcastInfo::get_originator () const
|
||||
{
|
||||
return info->originator;
|
||||
}
|
||||
|
||||
string
|
||||
BroadcastInfo::get_originator_ref () const
|
||||
{
|
||||
return info->originator_reference;
|
||||
}
|
||||
|
||||
bool
|
||||
BroadcastInfo::write_to_file (string const & filename)
|
||||
{
|
||||
SNDFILE * file = 0;
|
||||
SF_INFO info;
|
||||
|
||||
info.format = 0;
|
||||
|
||||
if (!(file = sf_open (filename.c_str(), SFM_RDWR, &info))) {
|
||||
update_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = write_to_file (file);
|
||||
|
||||
sf_close (file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
BroadcastInfo::write_to_file (SNDFILE* sf)
|
||||
{
|
||||
if (sf_command (sf, SFC_SET_BROADCAST_INFO, info, sizeof (*info)) != SF_TRUE) {
|
||||
update_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BroadcastInfo::set_description (string const & desc)
|
||||
{
|
||||
_has_info = true;
|
||||
|
||||
snprintf (info->description, sizeof (info->description), desc.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
BroadcastInfo::set_time_reference (int64_t when)
|
||||
{
|
||||
_has_info = true;
|
||||
|
||||
info->time_reference_high = (when >> 32);
|
||||
info->time_reference_low = (when & 0xffffffff);
|
||||
}
|
||||
|
||||
void
|
||||
BroadcastInfo::set_origination_time (struct tm * now)
|
||||
{
|
||||
_has_info = true;
|
||||
|
||||
if (now) {
|
||||
_time = *now;
|
||||
}
|
||||
|
||||
snprintf (info->origination_date, sizeof (info->origination_date), "%4d-%02d-%02d",
|
||||
_time.tm_year + 1900,
|
||||
_time.tm_mon + 1,
|
||||
_time.tm_mday);
|
||||
|
||||
snprintf (info->origination_time, sizeof (info->origination_time), "%02d:%02d:%02d",
|
||||
_time.tm_hour,
|
||||
_time.tm_min,
|
||||
_time.tm_sec);
|
||||
}
|
||||
|
||||
void
|
||||
BroadcastInfo::set_originator (string const & str)
|
||||
{
|
||||
_has_info = true;
|
||||
|
||||
if (!str.empty()) {
|
||||
snprintf (info->originator, sizeof (info->originator), str.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf (info->originator, sizeof (info->originator), Glib::get_real_name().c_str());
|
||||
}
|
||||
|
||||
void
|
||||
BroadcastInfo::set_originator_ref (string const & str)
|
||||
{
|
||||
_has_info = true;
|
||||
|
||||
if (!str.empty()) {
|
||||
snprintf (info->originator_reference, sizeof (info->originator_reference), str.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
/* random code is 9 digits */
|
||||
|
||||
int random_code = random() % 999999999;
|
||||
|
||||
/* Serial number is 12 chars */
|
||||
|
||||
std::ostringstream serial_number;
|
||||
serial_number << "ARDOUR" << "r" << std::setfill('0') << std::right << std::setw(5) << svn_revision;
|
||||
|
||||
snprintf (info->originator_reference, sizeof (info->originator_reference), "%2s%3s%12s%02d%02d%02d%9d",
|
||||
Config->get_bwf_country_code().c_str(),
|
||||
Config->get_bwf_organization_code().c_str(),
|
||||
serial_number.str().c_str(),
|
||||
_time.tm_hour,
|
||||
_time.tm_min,
|
||||
_time.tm_sec,
|
||||
random_code);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
BroadcastInfo::update_error ()
|
||||
{
|
||||
char errbuf[256];
|
||||
sf_error_str (0, errbuf, sizeof (errbuf) - 1);
|
||||
error = errbuf;
|
||||
}
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
204
libs/ardour/export_channel_configuration.cc
Normal file
204
libs/ardour/export_channel_configuration.cc
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/export_channel_configuration.h>
|
||||
|
||||
#include <ardour/export_handler.h>
|
||||
#include <ardour/export_filename.h>
|
||||
#include <ardour/export_processor.h>
|
||||
#include <ardour/export_timespan.h>
|
||||
|
||||
#include <ardour/audio_port.h>
|
||||
#include <ardour/export_failed.h>
|
||||
#include <ardour/midi_port.h>
|
||||
#include <pbd/pthread_utils.h>
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
/* ExportChannel */
|
||||
|
||||
ExportChannel::ExportChannel ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ExportChannel::~ExportChannel ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ExportChannel::read_ports (float * data, nframes_t frames) const
|
||||
{
|
||||
memset (data, 0, frames * sizeof (float));
|
||||
|
||||
for (iterator it = begin(); it != end(); ++it) {
|
||||
if (*it != 0) {
|
||||
Sample* port_buffer = (*it)->get_audio_buffer().data();
|
||||
|
||||
for (uint32_t i = 0; i < frames; ++i) {
|
||||
data[i] += (float) port_buffer[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ExportChannelConfiguration */
|
||||
|
||||
ExportChannelConfiguration::ExportChannelConfiguration (ExportStatus & status) :
|
||||
writer_thread (*this),
|
||||
status (status),
|
||||
files_written (false),
|
||||
split (false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ExportChannelConfiguration::~ExportChannelConfiguration ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
ExportChannelConfiguration::all_channels_have_ports ()
|
||||
{
|
||||
for (ChannelList::iterator it = channels.begin(); it != channels.end(); ++it) {
|
||||
if ((*it)->empty ()) { return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ExportChannelConfiguration::write_files (boost::shared_ptr<ExportProcessor> new_processor)
|
||||
{
|
||||
if (files_written || writer_thread.running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
files_written = true;
|
||||
|
||||
if (!timespan) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), _("No timespan registered to channel configuration when requesting files to be written"));
|
||||
}
|
||||
|
||||
/* Take a local copy of the processor to be used in the thread that is created below */
|
||||
|
||||
processor.reset (new_processor->copy());
|
||||
|
||||
/* Create new thread for post processing */
|
||||
|
||||
pthread_create (&writer_thread.thread, 0, _write_files, &writer_thread);
|
||||
writer_thread.running = true;
|
||||
pthread_detach (writer_thread.thread);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ExportChannelConfiguration::write_file ()
|
||||
{
|
||||
timespan->rewind ();
|
||||
nframes_t progress = 0;
|
||||
nframes_t timespan_length = timespan->get_length();
|
||||
|
||||
nframes_t frames = 2048; // TODO good block size ?
|
||||
nframes_t frames_read = 0;
|
||||
|
||||
float * channel_buffer = new float [frames];
|
||||
float * file_buffer = new float [channels.size() * frames];
|
||||
uint32_t channel_count = channels.size();
|
||||
uint32_t channel;
|
||||
|
||||
do {
|
||||
if (status.aborted()) { break; }
|
||||
|
||||
channel = 0;
|
||||
for (ChannelList::iterator it = channels.begin(); it != channels.end(); ++it) {
|
||||
|
||||
/* Get channel data */
|
||||
|
||||
frames_read = timespan->get_data (channel_buffer, frames, **it);
|
||||
|
||||
/* Interleave into file buffer */
|
||||
|
||||
for (uint32_t i = 0; i < frames_read; ++i) {
|
||||
file_buffer[channel + (channel_count * i)] = channel_buffer[i];
|
||||
}
|
||||
|
||||
++channel;
|
||||
}
|
||||
|
||||
progress += frames_read;
|
||||
status.progress = (float) progress / timespan_length;
|
||||
|
||||
} while (processor->process (file_buffer, frames_read) > 0);
|
||||
|
||||
delete [] channel_buffer;
|
||||
delete [] file_buffer;
|
||||
}
|
||||
|
||||
void *
|
||||
ExportChannelConfiguration::_write_files (void *arg)
|
||||
{
|
||||
|
||||
PBD::ThreadCreated (pthread_self(), "Export post-processing");
|
||||
|
||||
// cc can be trated like 'this'
|
||||
WriterThread & cc (*((WriterThread *) arg));
|
||||
|
||||
for (FileConfigList::iterator it = cc->file_configs.begin(); it != cc->file_configs.end(); ++it) {
|
||||
if (cc->status.aborted()) {
|
||||
break;
|
||||
}
|
||||
cc->processor->prepare (it->first, it->second, cc->channels.size(), cc->split, cc->timespan->get_start());
|
||||
cc->write_file (); // Writes tempfile
|
||||
cc->processor->prepare_post_processors ();
|
||||
cc->processor->write_files();
|
||||
}
|
||||
|
||||
cc.running = false;
|
||||
cc->files_written = true;
|
||||
cc->FilesWritten();
|
||||
|
||||
return 0; // avoid compiler warnings
|
||||
}
|
||||
|
||||
void
|
||||
ExportChannelConfiguration::register_with_timespan (TimespanPtr new_timespan)
|
||||
{
|
||||
timespan = new_timespan;
|
||||
|
||||
for (ChannelList::iterator it = channels.begin(); it != channels.end(); ++it) {
|
||||
timespan->register_channel (**it);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportChannelConfiguration::unregister_all ()
|
||||
{
|
||||
timespan.reset();
|
||||
processor.reset();
|
||||
file_configs.clear();
|
||||
files_written = false;
|
||||
}
|
||||
|
||||
} // namespace ARDOUR
|
||||
366
libs/ardour/export_file_io.cc
Normal file
366
libs/ardour/export_file_io.cc
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/export_file_io.h>
|
||||
#include <ardour/export_failed.h>
|
||||
#include <pbd/failed_constructor.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace PBD;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
/* SndfileWriterBase */
|
||||
|
||||
SndfileWriterBase::SndfileWriterBase (int channels, nframes_t samplerate, int format, string const & path) :
|
||||
ExportFileWriter (path)
|
||||
{
|
||||
char errbuf[256];
|
||||
|
||||
sf_info.channels = channels;
|
||||
sf_info.samplerate = samplerate;
|
||||
sf_info.format = format;
|
||||
|
||||
if (!sf_format_check (&sf_info)) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "Invalid format given for SndfileWriter!");
|
||||
}
|
||||
|
||||
if (path.length() == 0) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "No output file specified for SndFileWriter");
|
||||
}
|
||||
|
||||
/* TODO add checks that the directory path exists, and also
|
||||
check if we are overwriting an existing file...
|
||||
*/
|
||||
|
||||
// Open file TODO make sure we have enough disk space for the output
|
||||
if (path.compare ("temp")) {
|
||||
if ((sndfile = sf_open (path.c_str(), SFM_WRITE, &sf_info)) == 0) {
|
||||
sf_error_str (0, errbuf, sizeof (errbuf) - 1);
|
||||
throw ExportFailed (string_compose(_("Export: cannot open output file \"%1\""), path),
|
||||
string_compose(_("Export: cannot open output file \"%1\" for SndFileWriter (%2)"), path, errbuf));
|
||||
}
|
||||
} else {
|
||||
FILE * file;
|
||||
if (!(file = tmpfile ())) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "Cannot open tempfile");
|
||||
}
|
||||
sndfile = sf_open_fd (fileno(file), SFM_RDWR, &sf_info, true);
|
||||
}
|
||||
}
|
||||
|
||||
SndfileWriterBase::~SndfileWriterBase ()
|
||||
{
|
||||
sf_close (sndfile);
|
||||
}
|
||||
|
||||
/* SndfileWriter */
|
||||
|
||||
template <typename T>
|
||||
SndfileWriter<T>::SndfileWriter (int channels, nframes_t samplerate, int format, string const & path) :
|
||||
SndfileWriterBase (channels, samplerate, format, path)
|
||||
{
|
||||
// init write function
|
||||
init ();
|
||||
}
|
||||
|
||||
template <>
|
||||
void
|
||||
SndfileWriter<float>::init ()
|
||||
{
|
||||
write_func = &sf_writef_float;
|
||||
}
|
||||
|
||||
template <>
|
||||
void
|
||||
SndfileWriter<int>::init ()
|
||||
{
|
||||
write_func = &sf_writef_int;
|
||||
}
|
||||
|
||||
template <>
|
||||
void
|
||||
SndfileWriter<short>::init ()
|
||||
{
|
||||
write_func = &sf_writef_short;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
nframes_t
|
||||
SndfileWriter<T>::write (T * data, nframes_t frames)
|
||||
{
|
||||
char errbuf[256];
|
||||
nframes_t written = (*write_func) (sndfile, data, frames);
|
||||
if (written != frames) {
|
||||
sf_error_str (sndfile, errbuf, sizeof (errbuf) - 1);
|
||||
throw ExportFailed (_("Writing export file failed"), string_compose(_("Could not write data to output file (%1)"), errbuf));
|
||||
}
|
||||
|
||||
if (GraphSink<T>::end_of_input) {
|
||||
sf_write_sync (sndfile);
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
template class SndfileWriter<short>;
|
||||
template class SndfileWriter<int>;
|
||||
template class SndfileWriter<float>;
|
||||
|
||||
/* ExportTempFile */
|
||||
|
||||
ExportTempFile::ExportTempFile (uint32_t channels, nframes_t samplerate) :
|
||||
SndfileWriter<float> (channels, samplerate, SF_FORMAT_RAW | SF_FORMAT_FLOAT | SF_ENDIAN_FILE, "temp"),
|
||||
channels (channels),
|
||||
reading (false),
|
||||
start (0),
|
||||
end (0),
|
||||
beginning_processed (false),
|
||||
end_processed (false),
|
||||
silence_beginning (0),
|
||||
silence_end (0),
|
||||
end_set (false)
|
||||
{
|
||||
}
|
||||
|
||||
nframes_t
|
||||
ExportTempFile::read (float * data, nframes_t frames)
|
||||
{
|
||||
nframes_t frames_read = 0;
|
||||
nframes_t to_read = 0;
|
||||
sf_count_t read_status = 0;
|
||||
|
||||
/* Initialize state at first read */
|
||||
|
||||
if (!reading) {
|
||||
if (!end_set) {
|
||||
end = get_length();
|
||||
end_set = true;
|
||||
}
|
||||
locate_to (start);
|
||||
reading = true;
|
||||
}
|
||||
|
||||
/* Add silence to beginning */
|
||||
|
||||
if (silence_beginning > 0) {
|
||||
if (silence_beginning >= frames) {
|
||||
memset (data, 0, channels * frames * sizeof (float));
|
||||
silence_beginning -= frames;
|
||||
return frames;
|
||||
}
|
||||
|
||||
memset (data, 0, channels * silence_beginning * sizeof (float));
|
||||
|
||||
frames_read += silence_beginning;
|
||||
data += channels * silence_beginning;
|
||||
silence_beginning = 0;
|
||||
}
|
||||
|
||||
/* Read file, but don't read past end */
|
||||
|
||||
if (get_read_position() >= end) {
|
||||
// File already read, do nothing!
|
||||
} else {
|
||||
if ((get_read_position() + (frames - frames_read)) > end) {
|
||||
to_read = end - get_read_position();
|
||||
} else {
|
||||
to_read = frames - frames_read;
|
||||
}
|
||||
|
||||
read_status = sf_readf_float (sndfile, data, to_read);
|
||||
|
||||
frames_read += to_read;
|
||||
data += channels * to_read;
|
||||
}
|
||||
|
||||
/* Check for errors */
|
||||
|
||||
if (read_status != to_read) {
|
||||
throw ExportFailed (_("Reading export file failed"), _("Error reading temporary export file, export might not be complete!"));
|
||||
}
|
||||
|
||||
/* Add silence at end */
|
||||
|
||||
if (silence_end > 0) {
|
||||
to_read = frames - frames_read;
|
||||
if (silence_end < to_read) {
|
||||
to_read = silence_end;
|
||||
}
|
||||
|
||||
memset (data, 0, channels * to_read * sizeof (float));
|
||||
silence_end -= to_read;
|
||||
frames_read += to_read;
|
||||
}
|
||||
|
||||
return frames_read;
|
||||
}
|
||||
|
||||
nframes_t
|
||||
ExportTempFile::trim_beginning (bool yn)
|
||||
{
|
||||
if (!yn) {
|
||||
start = 0;
|
||||
return start;
|
||||
}
|
||||
|
||||
if (!beginning_processed) {
|
||||
process_beginning ();
|
||||
}
|
||||
|
||||
start = silent_frames_beginning;
|
||||
return start;
|
||||
|
||||
}
|
||||
|
||||
nframes_t
|
||||
ExportTempFile::trim_end (bool yn)
|
||||
{
|
||||
end_set = true;
|
||||
|
||||
if (!yn) {
|
||||
end = get_length();
|
||||
return end;
|
||||
}
|
||||
|
||||
if (!end_processed) {
|
||||
process_end ();
|
||||
}
|
||||
|
||||
end = silent_frames_end;
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ExportTempFile::process_beginning ()
|
||||
{
|
||||
nframes_t frames = 1024;
|
||||
nframes_t frames_read;
|
||||
float * buf = new float[channels * frames];
|
||||
|
||||
nframes_t pos = 0;
|
||||
locate_to (pos);
|
||||
|
||||
while ((frames_read = _read (buf, frames)) > 0) {
|
||||
for (nframes_t i = 0; i < frames_read; i++) {
|
||||
for (uint32_t chn = 0; chn < channels; ++chn) {
|
||||
if (buf[chn + i * channels] != 0.0f) {
|
||||
--pos;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
silent_frames_beginning = pos;
|
||||
beginning_processed = true;
|
||||
|
||||
delete [] buf;
|
||||
}
|
||||
|
||||
void
|
||||
ExportTempFile::process_end ()
|
||||
{
|
||||
nframes_t frames = 1024;
|
||||
nframes_t frames_read;
|
||||
float * buf = new float[channels * frames];
|
||||
|
||||
nframes_t pos = get_length() - 1;
|
||||
|
||||
while (pos > 0) {
|
||||
if (pos > frames) {
|
||||
locate_to (pos - frames);
|
||||
frames_read = _read (buf, frames);
|
||||
} else {
|
||||
// Last time reading
|
||||
locate_to (0);
|
||||
frames_read = _read (buf, pos);
|
||||
}
|
||||
|
||||
for (nframes_t i = frames_read; i > 0; --i) {
|
||||
for (uint32_t chn = 0; chn < channels; ++chn) {
|
||||
if (buf[chn + (i - 1) * channels] != 0.0f) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
silent_frames_end = pos;
|
||||
end_processed = true;
|
||||
|
||||
delete [] buf;
|
||||
}
|
||||
|
||||
void
|
||||
ExportTempFile::set_silence_beginning (nframes_t frames)
|
||||
{
|
||||
silence_beginning = frames;
|
||||
}
|
||||
|
||||
void
|
||||
ExportTempFile::set_silence_end (nframes_t frames)
|
||||
{
|
||||
silence_end = frames;
|
||||
}
|
||||
|
||||
sf_count_t
|
||||
ExportTempFile::get_length ()
|
||||
{
|
||||
sf_count_t pos = get_position();
|
||||
sf_count_t len = sf_seek (sndfile, 0, SEEK_END);
|
||||
locate_to (pos);
|
||||
return len;
|
||||
}
|
||||
|
||||
sf_count_t
|
||||
ExportTempFile::get_position ()
|
||||
{
|
||||
return sf_seek (sndfile, 0, SEEK_CUR);
|
||||
}
|
||||
|
||||
sf_count_t
|
||||
ExportTempFile::get_read_position ()
|
||||
{
|
||||
return sf_seek (sndfile, 0, SEEK_CUR | SFM_READ);
|
||||
}
|
||||
|
||||
sf_count_t
|
||||
ExportTempFile::locate_to (nframes_t frames)
|
||||
{
|
||||
return sf_seek (sndfile, frames, SEEK_SET);
|
||||
}
|
||||
|
||||
sf_count_t
|
||||
ExportTempFile::_read (float * data, nframes_t frames)
|
||||
{
|
||||
return sf_readf_float (sndfile, data, frames);
|
||||
}
|
||||
|
||||
};
|
||||
362
libs/ardour/export_filename.cc
Normal file
362
libs/ardour/export_filename.cc
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/export_filename.h>
|
||||
|
||||
#include <pbd/xml++.h>
|
||||
#include <pbd/convert.h>
|
||||
#include <pbd/enumwriter.h>
|
||||
|
||||
#include <ardour/session.h>
|
||||
#include <ardour/session_directory.h>
|
||||
#include <ardour/export_timespan.h>
|
||||
#include <ardour/export_format_specification.h>
|
||||
#include <ardour/export_channel_configuration.h>
|
||||
#include <ardour/export_failed.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace PBD;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
ExportFilename::ExportFilename (Session & session) :
|
||||
include_label (false),
|
||||
include_session (false),
|
||||
include_revision (false),
|
||||
include_channel_config (false),
|
||||
include_channel (false),
|
||||
include_timespan (true), // Include timespan name always
|
||||
include_time (false),
|
||||
include_date (false),
|
||||
session (session),
|
||||
revision (1)
|
||||
{
|
||||
time_t rawtime;
|
||||
std::time (&rawtime);
|
||||
time_struct = localtime (&rawtime);
|
||||
|
||||
folder = session.session_directory().export_path().to_string();
|
||||
|
||||
XMLNode * instant_node = session.instant_xml ("ExportFilename");
|
||||
if (instant_node) {
|
||||
set_state (*instant_node);
|
||||
}
|
||||
}
|
||||
|
||||
XMLNode &
|
||||
ExportFilename::get_state ()
|
||||
{
|
||||
XMLNode * node = new XMLNode ("ExportFilename");
|
||||
XMLNode * child;
|
||||
|
||||
FieldPair dir = analyse_folder();
|
||||
child = node->add_child ("Folder");
|
||||
child->add_property ("relative", dir.first ? "true" : "false");
|
||||
child->add_property ("path", dir.second);
|
||||
|
||||
add_field (node, "label", include_label, label);
|
||||
add_field (node, "session", include_session);
|
||||
add_field (node, "revision", include_revision);
|
||||
add_field (node, "time", include_time, enum_2_string (time_format));
|
||||
add_field (node, "date", include_date, enum_2_string (date_format));
|
||||
|
||||
XMLNode * instant_node = new XMLNode ("ExportRevision");
|
||||
instant_node->add_property ("revision", to_string (revision, std::dec));
|
||||
session.add_instant_xml (*instant_node);
|
||||
|
||||
return *node;
|
||||
}
|
||||
|
||||
int
|
||||
ExportFilename::set_state (const XMLNode & node)
|
||||
{
|
||||
XMLNode * child;
|
||||
XMLProperty * prop;
|
||||
FieldPair pair;
|
||||
|
||||
child = node.child ("Folder");
|
||||
if (!child) { return -1; }
|
||||
|
||||
folder = "";
|
||||
|
||||
if ((prop = child->property ("relative"))) {
|
||||
if (!prop->value().compare ("true")) {
|
||||
folder = session.session_directory().root_path().to_string();
|
||||
}
|
||||
}
|
||||
|
||||
if ((prop = child->property ("path"))) {
|
||||
folder += prop->value();
|
||||
}
|
||||
|
||||
|
||||
pair = get_field (node, "label");
|
||||
include_label = pair.first;
|
||||
label = pair.second;
|
||||
|
||||
pair = get_field (node, "session");
|
||||
include_session = pair.first;
|
||||
|
||||
pair = get_field (node, "revision");
|
||||
include_revision = pair.first;
|
||||
|
||||
pair = get_field (node, "time");
|
||||
include_time = pair.first;
|
||||
time_format = (TimeFormat) string_2_enum (pair.second, time_format);
|
||||
|
||||
pair = get_field (node, "date");
|
||||
include_date = pair.first;
|
||||
date_format = (DateFormat) string_2_enum (pair.second, date_format);
|
||||
|
||||
XMLNode * instant_node = session.instant_xml ("ExportRevision");
|
||||
if (instant_node && (prop = instant_node->property ("revision"))) {
|
||||
revision = atoi (prop->value());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ustring
|
||||
ExportFilename::get_path (FormatPtr format) const
|
||||
{
|
||||
ustring path = folder;
|
||||
bool filename_empty = true;
|
||||
|
||||
path += "/";
|
||||
|
||||
if (include_session) {
|
||||
path += filename_empty ? "" : "_";
|
||||
path += session.name();
|
||||
filename_empty = false;
|
||||
}
|
||||
|
||||
if (include_label) {
|
||||
path += filename_empty ? "" : "_";
|
||||
path += label;
|
||||
filename_empty = false;
|
||||
}
|
||||
|
||||
if (include_revision) {
|
||||
path += filename_empty ? "" : "_";
|
||||
path += "r";
|
||||
path += to_string (revision, std::dec);
|
||||
filename_empty = false;
|
||||
}
|
||||
|
||||
if (include_timespan && timespan) {
|
||||
path += filename_empty ? "" : "_";
|
||||
path += timespan->name();
|
||||
filename_empty = false;
|
||||
}
|
||||
|
||||
if (include_channel_config && channel_config) {
|
||||
path += filename_empty ? "" : "_";
|
||||
path += channel_config->name();
|
||||
filename_empty = false;
|
||||
}
|
||||
|
||||
if (include_channel) {
|
||||
path += filename_empty ? "" : "_";
|
||||
path += "channel";
|
||||
path += to_string (channel, std::dec);
|
||||
filename_empty = false;
|
||||
}
|
||||
|
||||
if (include_date) {
|
||||
path += filename_empty ? "" : "_";
|
||||
path += get_date_format_str (date_format);
|
||||
filename_empty = false;
|
||||
}
|
||||
|
||||
if (include_time) {
|
||||
path += filename_empty ? "" : "_";
|
||||
path += get_time_format_str (time_format);
|
||||
filename_empty = false;
|
||||
}
|
||||
|
||||
path += ".";
|
||||
path += format->extension ();
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
ustring
|
||||
ExportFilename::get_time_format_str (TimeFormat format) const
|
||||
{
|
||||
switch ( format ) {
|
||||
case T_None:
|
||||
return _("No Time");
|
||||
|
||||
case T_NoDelim:
|
||||
return get_formatted_time ("%H%M");
|
||||
|
||||
case T_Delim:
|
||||
return get_formatted_time ("%H.%M");
|
||||
|
||||
default:
|
||||
return _("Invalid time format");
|
||||
}
|
||||
}
|
||||
|
||||
ustring
|
||||
ExportFilename::get_date_format_str (DateFormat format) const
|
||||
{
|
||||
switch (format) {
|
||||
case D_None:
|
||||
return _("No Date");
|
||||
|
||||
case D_BE:
|
||||
return get_formatted_time ("%Y%m%d");
|
||||
|
||||
case D_ISO:
|
||||
return get_formatted_time ("%Y-%m-%d");
|
||||
|
||||
case D_BEShortY:
|
||||
return get_formatted_time ("%y%m%d");
|
||||
|
||||
case D_ISOShortY:
|
||||
return get_formatted_time ("%y-%m-%d");
|
||||
|
||||
default:
|
||||
return _("Invalid date format");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFilename::set_time_format (TimeFormat format)
|
||||
{
|
||||
time_format = format;
|
||||
|
||||
if (format == T_None) {
|
||||
include_time = false;
|
||||
} else {
|
||||
include_time = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFilename::set_date_format (DateFormat format)
|
||||
{
|
||||
date_format = format;
|
||||
|
||||
if (format == D_None) {
|
||||
include_date = false;
|
||||
} else {
|
||||
include_date = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFilename::set_label (ustring value)
|
||||
{
|
||||
label = value;
|
||||
include_label = !value.compare ("");
|
||||
}
|
||||
|
||||
bool
|
||||
ExportFilename::set_folder (ustring path)
|
||||
{
|
||||
// TODO check folder existence
|
||||
folder = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
ustring
|
||||
ExportFilename::get_formatted_time (ustring const & format) const
|
||||
{
|
||||
char buffer [80];
|
||||
strftime (buffer, 80, format.c_str(), time_struct);
|
||||
|
||||
ustring return_value (buffer);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void
|
||||
ExportFilename::add_field (XMLNode * node, ustring const & name, bool enabled, ustring const & value)
|
||||
{
|
||||
XMLNode * child = node->add_child ("Field");
|
||||
|
||||
if (!child) {
|
||||
std::cerr << "Error adding a field to ExportFilename XML-tree" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
child->add_property ("name", name);
|
||||
child->add_property ("enabled", enabled ? "true" : "false");
|
||||
if (!value.empty()) {
|
||||
child->add_property ("value", value);
|
||||
}
|
||||
}
|
||||
|
||||
ExportFilename::FieldPair
|
||||
ExportFilename::get_field (XMLNode const & node, ustring const & name)
|
||||
{
|
||||
FieldPair pair;
|
||||
pair.first = false;
|
||||
|
||||
XMLNodeList children = node.children();
|
||||
|
||||
for (XMLNodeList::iterator it = children.begin(); it != children.end(); ++it) {
|
||||
XMLProperty * prop = (*it)->property ("name");
|
||||
if (prop && !prop->value().compare (name)) {
|
||||
|
||||
prop = (*it)->property ("enabled");
|
||||
if (prop && !prop->value().compare ("true")) {
|
||||
pair.first = true;
|
||||
} else {
|
||||
pair.first = false;
|
||||
}
|
||||
|
||||
prop = (*it)->property ("value");
|
||||
if (prop) {
|
||||
pair.second = prop->value();
|
||||
}
|
||||
|
||||
return pair;
|
||||
}
|
||||
}
|
||||
|
||||
return pair;
|
||||
}
|
||||
|
||||
ExportFilename::FieldPair
|
||||
ExportFilename::analyse_folder ()
|
||||
{
|
||||
FieldPair pair;
|
||||
|
||||
ustring session_dir = session.session_directory().root_path().to_string();
|
||||
ustring::size_type session_dir_len = session_dir.length();
|
||||
|
||||
ustring folder_beginning = folder.substr (0, session_dir_len);
|
||||
|
||||
if (!folder_beginning.compare (session_dir)) {
|
||||
pair.first = true;
|
||||
pair.second = folder.substr (session_dir_len);
|
||||
} else {
|
||||
pair.first = false;
|
||||
pair.second = folder;
|
||||
}
|
||||
|
||||
return pair;
|
||||
}
|
||||
|
||||
} // namespace ARDOUR
|
||||
201
libs/ardour/export_format_base.cc
Normal file
201
libs/ardour/export_format_base.cc
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/export_format_base.h>
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
void
|
||||
ExportFormatBase::SelectableCompatible::set_selected (bool value)
|
||||
{
|
||||
if (_selected != value) {
|
||||
_selected = value;
|
||||
SelectChanged (value);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatBase::SelectableCompatible::set_compatible (bool value)
|
||||
{
|
||||
if (_compatible != value) {
|
||||
_compatible = value;
|
||||
CompatibleChanged (value);
|
||||
}
|
||||
if (!value) {
|
||||
set_selected (false);
|
||||
}
|
||||
}
|
||||
|
||||
ExportFormatBase::ExportFormatBase ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ExportFormatBase::ExportFormatBase (ExportFormatBase const & other) :
|
||||
sample_formats (other.sample_formats),
|
||||
endiannesses (other.endiannesses),
|
||||
sample_rates (other.sample_rates),
|
||||
format_ids (other.format_ids),
|
||||
qualities (other.qualities)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ExportFormatBase::~ExportFormatBase ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
boost::shared_ptr<ExportFormatBase>
|
||||
ExportFormatBase::get_intersection (ExportFormatBase const & other) const
|
||||
{
|
||||
return do_set_operation (other, SetIntersection);
|
||||
}
|
||||
|
||||
boost::shared_ptr<ExportFormatBase>
|
||||
ExportFormatBase::get_difference (ExportFormatBase const & other) const
|
||||
{
|
||||
return do_set_operation (other, SetDifference);
|
||||
}
|
||||
|
||||
boost::shared_ptr<ExportFormatBase>
|
||||
ExportFormatBase::get_union (ExportFormatBase const & other) const
|
||||
{
|
||||
return do_set_operation (other, SetUnion);
|
||||
}
|
||||
|
||||
boost::shared_ptr<ExportFormatBase>
|
||||
ExportFormatBase::do_set_operation (ExportFormatBase const & other, SetOperation operation) const
|
||||
{
|
||||
boost::shared_ptr<ExportFormatBase> result (new ExportFormatBase ());
|
||||
|
||||
/* Sets */
|
||||
|
||||
// Endiannesses
|
||||
{
|
||||
EndianSet::const_iterator start1 = endiannesses.begin();
|
||||
EndianSet::const_iterator end1 = endiannesses.end();
|
||||
EndianSet::const_iterator start2 = other.endiannesses.begin();
|
||||
EndianSet::const_iterator end2 = other.endiannesses.end();
|
||||
std::insert_iterator<EndianSet> insert (result->endiannesses, result->endiannesses.begin());
|
||||
|
||||
switch (operation) {
|
||||
case SetIntersection:
|
||||
std::set_intersection (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
case SetDifference:
|
||||
std::set_difference (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
case SetUnion:
|
||||
std::set_union (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Sample formats
|
||||
{
|
||||
SampleFormatSet::const_iterator start1 = sample_formats.begin();
|
||||
SampleFormatSet::const_iterator end1 = sample_formats.end();
|
||||
SampleFormatSet::const_iterator start2 = other.sample_formats.begin();
|
||||
SampleFormatSet::const_iterator end2 = other.sample_formats.end();
|
||||
std::insert_iterator<SampleFormatSet> insert (result->sample_formats, result->sample_formats.begin());
|
||||
|
||||
switch (operation) {
|
||||
case SetIntersection:
|
||||
std::set_intersection (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
case SetDifference:
|
||||
std::set_difference (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
case SetUnion:
|
||||
std::set_union (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sample rates
|
||||
{
|
||||
SampleRateSet::const_iterator start1 = sample_rates.begin();
|
||||
SampleRateSet::const_iterator end1 = sample_rates.end();
|
||||
SampleRateSet::const_iterator start2 = other.sample_rates.begin();
|
||||
SampleRateSet::const_iterator end2 = other.sample_rates.end();
|
||||
std::insert_iterator<SampleRateSet> insert (result->sample_rates, result->sample_rates.begin());
|
||||
|
||||
switch (operation) {
|
||||
case SetIntersection:
|
||||
std::set_intersection (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
case SetDifference:
|
||||
std::set_difference (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
case SetUnion:
|
||||
std::set_union (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Format ids
|
||||
{
|
||||
FormatSet::const_iterator start1 = format_ids.begin();
|
||||
FormatSet::const_iterator end1 = format_ids.end();
|
||||
FormatSet::const_iterator start2 = other.format_ids.begin();
|
||||
FormatSet::const_iterator end2 = other.format_ids.end();
|
||||
std::insert_iterator<FormatSet> insert (result->format_ids, result->format_ids.begin());
|
||||
|
||||
switch (operation) {
|
||||
case SetIntersection:
|
||||
std::set_intersection (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
case SetDifference:
|
||||
std::set_difference (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
case SetUnion:
|
||||
std::set_union (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Qualities
|
||||
{
|
||||
QualitySet::const_iterator start1 = qualities.begin();
|
||||
QualitySet::const_iterator end1 = qualities.end();
|
||||
QualitySet::const_iterator start2 = other.qualities.begin();
|
||||
QualitySet::const_iterator end2 = other.qualities.end();
|
||||
std::insert_iterator<QualitySet> insert (result->qualities, result->qualities.begin());
|
||||
|
||||
switch (operation) {
|
||||
case SetIntersection:
|
||||
std::set_intersection (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
case SetDifference:
|
||||
std::set_difference (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
case SetUnion:
|
||||
std::set_union (start1, end1, start2, end2, insert);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}; // namespace ARDOUR
|
||||
749
libs/ardour/export_format_manager.cc
Normal file
749
libs/ardour/export_format_manager.cc
Normal file
|
|
@ -0,0 +1,749 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/export_format_manager.h>
|
||||
|
||||
#include <ardour/types.h>
|
||||
#include <ardour/export_format_specification.h>
|
||||
#include <ardour/export_format_compatibility.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
ExportFormatManager::ExportFormatManager (SpecPtr specification) :
|
||||
pending_selection_change (false),
|
||||
universal_set (new ExportFormatBase ())
|
||||
{
|
||||
current_selection = specification;
|
||||
|
||||
init_compatibilities ();
|
||||
init_qualities ();
|
||||
init_formats ();
|
||||
init_sample_rates ();
|
||||
}
|
||||
|
||||
ExportFormatManager::~ExportFormatManager ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::init_compatibilities ()
|
||||
{
|
||||
CompatPtr c_ptr;
|
||||
|
||||
c_ptr.reset (new ExportFormatCompatibility (_("CD")));
|
||||
c_ptr->add_sample_rate (ExportFormatBase::SR_44_1);
|
||||
c_ptr->add_format_id (ExportFormatBase::F_WAV);
|
||||
c_ptr->add_format_id (ExportFormatBase::F_AIFF);
|
||||
c_ptr->add_quality (ExportFormatBase::Q_LosslessLinear);
|
||||
c_ptr->add_sample_format (ExportFormatBase::SF_16);
|
||||
c_ptr->add_endianness (ExportFormatBase::E_FileDefault);
|
||||
add_compatibility (c_ptr);
|
||||
|
||||
c_ptr.reset (new ExportFormatCompatibility (_("DVD-A")));
|
||||
c_ptr->add_sample_rate (ExportFormatBase::SR_44_1);
|
||||
c_ptr->add_sample_rate (ExportFormatBase::SR_48);
|
||||
c_ptr->add_sample_rate (ExportFormatBase::SR_88_2);
|
||||
c_ptr->add_sample_rate (ExportFormatBase::SR_96);
|
||||
c_ptr->add_sample_rate (ExportFormatBase::SR_192);
|
||||
c_ptr->add_format_id (ExportFormatBase::F_WAV);
|
||||
c_ptr->add_format_id (ExportFormatBase::F_AIFF);
|
||||
c_ptr->add_quality (ExportFormatBase::Q_LosslessLinear);
|
||||
c_ptr->add_sample_format (ExportFormatBase::SF_16);
|
||||
c_ptr->add_sample_format (ExportFormatBase::SF_24);
|
||||
c_ptr->add_endianness (ExportFormatBase::E_FileDefault);
|
||||
add_compatibility (c_ptr);
|
||||
|
||||
c_ptr.reset (new ExportFormatCompatibility (_("iPod")));
|
||||
c_ptr->add_sample_rate (ExportFormatBase::SR_44_1);
|
||||
c_ptr->add_sample_rate (ExportFormatBase::SR_48);
|
||||
c_ptr->add_format_id (ExportFormatBase::F_WAV);
|
||||
c_ptr->add_format_id (ExportFormatBase::F_AIFF);
|
||||
c_ptr->add_quality (ExportFormatBase::Q_LosslessLinear);
|
||||
c_ptr->add_sample_format (ExportFormatBase::SF_16);
|
||||
c_ptr->add_sample_format (ExportFormatBase::SF_24);
|
||||
c_ptr->add_endianness (ExportFormatBase::E_FileDefault);
|
||||
add_compatibility (c_ptr);
|
||||
|
||||
c_ptr.reset (new ExportFormatCompatibility (_("Something else")));
|
||||
c_ptr->add_sample_rate (ExportFormatBase::SR_44_1);
|
||||
c_ptr->add_sample_rate (ExportFormatBase::SR_48);
|
||||
c_ptr->add_format_id (ExportFormatBase::F_WAV);
|
||||
c_ptr->add_format_id (ExportFormatBase::F_AIFF);
|
||||
c_ptr->add_format_id (ExportFormatBase::F_AU);
|
||||
c_ptr->add_format_id (ExportFormatBase::F_FLAC);
|
||||
c_ptr->add_quality (ExportFormatBase::Q_LosslessLinear);
|
||||
c_ptr->add_quality (ExportFormatBase::Q_LosslessCompression);
|
||||
c_ptr->add_sample_format (ExportFormatBase::SF_16);
|
||||
c_ptr->add_sample_format (ExportFormatBase::SF_24);
|
||||
c_ptr->add_sample_format (ExportFormatBase::SF_32);
|
||||
c_ptr->add_endianness (ExportFormatBase::E_FileDefault);
|
||||
add_compatibility (c_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::init_qualities ()
|
||||
{
|
||||
add_quality (QualityPtr (new QualityState (ExportFormatBase::Q_Any, _("Any"))));
|
||||
add_quality (QualityPtr (new QualityState (ExportFormatBase::Q_LosslessLinear, _("Lossless (linear PCM)"))));
|
||||
add_quality (QualityPtr (new QualityState (ExportFormatBase::Q_LossyCompression, _("Lossy compression"))));
|
||||
add_quality (QualityPtr (new QualityState (ExportFormatBase::Q_LosslessCompression, _("Lossless compression"))));
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::init_formats ()
|
||||
{
|
||||
FormatPtr f_ptr;
|
||||
ExportFormatLinear * fl_ptr;
|
||||
|
||||
f_ptr.reset (fl_ptr = new ExportFormatLinear ("AIFF", ExportFormatBase::F_AIFF));
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_U8);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_8);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_16);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_24);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_32);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_Float);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_Double);
|
||||
fl_ptr->set_default_sample_format (ExportFormatBase::SF_16);
|
||||
fl_ptr->set_extension ("aiff");
|
||||
add_format (f_ptr);
|
||||
|
||||
f_ptr.reset (fl_ptr = new ExportFormatLinear ("AU", ExportFormatBase::F_AU));
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_8);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_16);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_24);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_32);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_Float);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_Double);
|
||||
fl_ptr->set_default_sample_format (ExportFormatBase::SF_16);
|
||||
fl_ptr->set_extension ("au");
|
||||
add_format (f_ptr);
|
||||
|
||||
f_ptr.reset (new ExportFormatBWF ());
|
||||
add_format (f_ptr);
|
||||
|
||||
f_ptr.reset (fl_ptr = new ExportFormatLinear ("IRCAM", ExportFormatBase::F_IRCAM));
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_16);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_24);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_32);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_Float);
|
||||
fl_ptr->set_default_sample_format (ExportFormatBase::SF_24);
|
||||
fl_ptr->set_extension ("sf");
|
||||
add_format (f_ptr);
|
||||
|
||||
f_ptr.reset (fl_ptr = new ExportFormatLinear ("WAV", ExportFormatBase::F_WAV));
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_U8);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_16);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_24);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_32);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_Float);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_Double);
|
||||
fl_ptr->set_default_sample_format (ExportFormatBase::SF_16);
|
||||
fl_ptr->set_extension ("wav");
|
||||
add_format (f_ptr);
|
||||
|
||||
f_ptr.reset (fl_ptr = new ExportFormatLinear ("W64", ExportFormatBase::F_W64));
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_U8);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_16);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_24);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_32);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_Float);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_Double);
|
||||
fl_ptr->set_default_sample_format (ExportFormatBase::SF_Double);
|
||||
fl_ptr->set_extension ("w64");
|
||||
add_format (f_ptr);
|
||||
|
||||
f_ptr.reset (fl_ptr = new ExportFormatLinear ("RAW", ExportFormatBase::F_RAW));
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_U8);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_8);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_16);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_24);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_32);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_Float);
|
||||
fl_ptr->add_sample_format (ExportFormatBase::SF_Double);
|
||||
fl_ptr->set_default_sample_format (ExportFormatBase::SF_Float);
|
||||
fl_ptr->set_extension ("raw");
|
||||
add_format (f_ptr);
|
||||
|
||||
f_ptr.reset (new ExportFormatOggVorbis ());
|
||||
add_format (f_ptr);
|
||||
|
||||
f_ptr.reset (new ExportFormatFLAC ());
|
||||
add_format (f_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::init_sample_rates ()
|
||||
{
|
||||
add_sample_rate (SampleRatePtr (new SampleRateState (ExportFormatBase::SR_22_05, "22,05 kHz")));
|
||||
add_sample_rate (SampleRatePtr (new SampleRateState (ExportFormatBase::SR_44_1, "44,1 kHz")));
|
||||
add_sample_rate (SampleRatePtr (new SampleRateState (ExportFormatBase::SR_48, "48 kHz")));
|
||||
add_sample_rate (SampleRatePtr (new SampleRateState (ExportFormatBase::SR_88_2, "88,2 kHz")));
|
||||
add_sample_rate (SampleRatePtr (new SampleRateState (ExportFormatBase::SR_96, "96 kHz")));
|
||||
add_sample_rate (SampleRatePtr (new SampleRateState (ExportFormatBase::SR_192, "192 kHz")));
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::add_compatibility (CompatPtr ptr)
|
||||
{
|
||||
compatibilities.push_back (ptr);
|
||||
ptr->SelectChanged.connect (sigc::bind (sigc::mem_fun (*this, &ExportFormatManager::change_compatibility_selection), WeakCompatPtr (ptr)));
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::add_quality (QualityPtr ptr)
|
||||
{
|
||||
ptr->SelectChanged.connect (sigc::bind (sigc::mem_fun (*this, &ExportFormatManager::change_quality_selection), WeakQualityPtr (ptr)));
|
||||
qualities.push_back (ptr);
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::add_format (FormatPtr ptr)
|
||||
{
|
||||
formats.push_back (ptr);
|
||||
ptr->SelectChanged.connect (sigc::bind (sigc::mem_fun (*this, &ExportFormatManager::change_format_selection), WeakFormatPtr (ptr)));
|
||||
universal_set = universal_set->get_union (*ptr);
|
||||
|
||||
/* Encoding options */
|
||||
|
||||
boost::shared_ptr<HasSampleFormat> hsf;
|
||||
|
||||
if (hsf = boost::dynamic_pointer_cast<HasSampleFormat> (ptr)) {
|
||||
hsf->SampleFormatSelectChanged.connect (sigc::mem_fun (*this, &ExportFormatManager::change_sample_format_selection));
|
||||
hsf->DitherTypeSelectChanged.connect (sigc::mem_fun (*this, &ExportFormatManager::change_dither_type_selection));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::add_sample_rate (SampleRatePtr ptr)
|
||||
{
|
||||
ptr->SelectChanged.connect (sigc::bind (sigc::mem_fun (*this, &ExportFormatManager::change_sample_rate_selection), WeakSampleRatePtr (ptr)));
|
||||
sample_rates.push_back (ptr);
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::set_name (Glib::ustring name)
|
||||
{
|
||||
current_selection->set_name (name);
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_src_quality (ExportFormatBase::SRCQuality value)
|
||||
{
|
||||
current_selection->set_src_quality (value);
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_trim_beginning (bool value)
|
||||
{
|
||||
current_selection->set_trim_beginning (value);
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_silence_beginning (AnyTime const & time)
|
||||
{
|
||||
current_selection->set_silence_beginning (time);
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_trim_end (bool value)
|
||||
{
|
||||
current_selection->set_trim_end (value);
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_silence_end (AnyTime const & time)
|
||||
{
|
||||
current_selection->set_silence_end (time);
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_normalize (bool value)
|
||||
{
|
||||
current_selection->set_normalize (value);
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_normalize_target (float value)
|
||||
{
|
||||
current_selection->set_normalize_target (value);
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_tagging (bool tag)
|
||||
{
|
||||
current_selection->set_tag (tag);
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::change_compatibility_selection (bool select, WeakCompatPtr const & compat)
|
||||
{
|
||||
bool do_selection_changed = !pending_selection_change;
|
||||
if (!pending_selection_change) {
|
||||
pending_selection_change = true;
|
||||
}
|
||||
|
||||
CompatPtr ptr = compat.lock();
|
||||
|
||||
if (ptr && select) {
|
||||
select_compatibility (ptr);
|
||||
}
|
||||
|
||||
if (do_selection_changed) {
|
||||
selection_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::change_quality_selection (bool select, WeakQualityPtr const & quality)
|
||||
{
|
||||
QualityPtr ptr = quality.lock ();
|
||||
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (select) {
|
||||
select_quality (ptr);
|
||||
} else if (ptr->quality == current_selection->quality()) {
|
||||
ptr.reset();
|
||||
select_quality (ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::change_format_selection (bool select, WeakFormatPtr const & format)
|
||||
{
|
||||
FormatPtr ptr = format.lock();
|
||||
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (select) {
|
||||
select_format (ptr);
|
||||
} else if (ptr->get_format_id() == current_selection->format_id()) {
|
||||
ptr.reset();
|
||||
select_format (ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::change_sample_rate_selection (bool select, WeakSampleRatePtr const & rate)
|
||||
{
|
||||
SampleRatePtr ptr = rate.lock();
|
||||
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (select) {
|
||||
select_sample_rate (ptr);
|
||||
} else if (ptr->rate == current_selection->sample_rate()) {
|
||||
ptr.reset();
|
||||
select_sample_rate (ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::change_sample_format_selection (bool select, WeakSampleFormatPtr const & format)
|
||||
{
|
||||
SampleFormatPtr ptr = format.lock();
|
||||
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (select) {
|
||||
select_sample_format (ptr);
|
||||
} else if (ptr->format == current_selection->sample_format()) {
|
||||
ptr.reset();
|
||||
select_sample_format (ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::change_dither_type_selection (bool select, WeakDitherTypePtr const & type)
|
||||
{
|
||||
DitherTypePtr ptr = type.lock();
|
||||
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (select) {
|
||||
select_dither_type (ptr);
|
||||
} else if (ptr->type == current_selection->dither_type()) {
|
||||
ptr.reset();
|
||||
select_dither_type (ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_compatibility (WeakCompatPtr const & compat)
|
||||
{
|
||||
/* Calculate compatibility intersection for the selection */
|
||||
|
||||
FormatBasePtr compat_intersect = get_compatibility_intersection ();
|
||||
|
||||
/* Unselect incompatible items */
|
||||
|
||||
boost::shared_ptr<ExportFormatBase> select_intersect;
|
||||
|
||||
select_intersect = compat_intersect->get_intersection (*current_selection);
|
||||
if (select_intersect->qualities_empty()) {
|
||||
select_quality (QualityPtr());
|
||||
}
|
||||
|
||||
select_intersect = compat_intersect->get_intersection (*current_selection);
|
||||
if (select_intersect->formats_empty()) {
|
||||
select_format (FormatPtr());
|
||||
}
|
||||
|
||||
select_intersect = compat_intersect->get_intersection (*current_selection);
|
||||
if (select_intersect->sample_rates_empty()) {
|
||||
select_sample_rate (SampleRatePtr());
|
||||
}
|
||||
|
||||
select_intersect = compat_intersect->get_intersection (*current_selection);
|
||||
if (select_intersect->sample_formats_empty()) {
|
||||
select_sample_format (SampleFormatPtr());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_quality (QualityPtr const & quality)
|
||||
{
|
||||
bool do_selection_changed = !pending_selection_change;
|
||||
if (!pending_selection_change) {
|
||||
pending_selection_change = true;
|
||||
}
|
||||
|
||||
if (quality) {
|
||||
current_selection->set_quality (quality->quality);
|
||||
|
||||
/* Deselect format if it is incompatible */
|
||||
|
||||
FormatPtr format = get_selected_format();
|
||||
if (format && !format->has_quality (quality->quality)) {
|
||||
format->set_selected (false);
|
||||
}
|
||||
|
||||
} else {
|
||||
current_selection->set_quality (ExportFormatBase::Q_None);
|
||||
|
||||
QualityPtr current_quality = get_selected_quality();
|
||||
if (current_quality) {
|
||||
current_quality->set_selected (false);
|
||||
}
|
||||
|
||||
/* Note:
|
||||
* A quality is never explicitly deselected without also deselecting the format
|
||||
* so we don't need to deselect the format here.
|
||||
* doing so causes extra complications
|
||||
*/
|
||||
}
|
||||
|
||||
if (do_selection_changed) {
|
||||
selection_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_format (FormatPtr const & format)
|
||||
{
|
||||
bool do_selection_changed = !pending_selection_change;
|
||||
if (!pending_selection_change) {
|
||||
pending_selection_change = true;
|
||||
}
|
||||
|
||||
current_selection->set_format (format);
|
||||
|
||||
if (format) {
|
||||
|
||||
/* Slect right quality for format */
|
||||
|
||||
ExportFormatBase::Quality quality = format->get_quality();
|
||||
for (QualityList::iterator it = qualities.begin (); it != qualities.end (); ++it) {
|
||||
if ((*it)->quality == quality) {
|
||||
(*it)->set_selected (true);
|
||||
} else {
|
||||
(*it)->set_selected (false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle sample formats */
|
||||
|
||||
ExportFormatBase::SampleFormat format_to_select;
|
||||
if (format->sample_format_is_compatible (current_selection->sample_format())) {
|
||||
format_to_select = current_selection->sample_format();
|
||||
} else {
|
||||
format_to_select = format->default_sample_format();
|
||||
}
|
||||
|
||||
boost::shared_ptr<HasSampleFormat> hsf;
|
||||
if ((hsf = boost::dynamic_pointer_cast<HasSampleFormat> (format))) {
|
||||
SampleFormatList sample_formats = hsf->get_sample_formats();
|
||||
for (SampleFormatList::iterator it = sample_formats.begin (); it != sample_formats.end (); ++it) {
|
||||
if ((*it)->format == format_to_select) {
|
||||
(*it)->set_selected (true);
|
||||
} else {
|
||||
(*it)->set_selected (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current_selection->set_sample_format (format_to_select);
|
||||
|
||||
} else {
|
||||
FormatPtr current_format = get_selected_format ();
|
||||
if (current_format) {
|
||||
current_format->set_selected (false);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_selection_changed) {
|
||||
selection_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_sample_rate (SampleRatePtr const & rate)
|
||||
{
|
||||
|
||||
bool do_selection_changed = !pending_selection_change;
|
||||
if (!pending_selection_change) {
|
||||
pending_selection_change = true;
|
||||
}
|
||||
|
||||
if (rate) {
|
||||
current_selection->set_sample_rate (rate->rate);
|
||||
} else {
|
||||
current_selection->set_sample_rate (ExportFormatBase::SR_None);
|
||||
|
||||
SampleRatePtr current_rate = get_selected_sample_rate();
|
||||
if (current_rate) {
|
||||
current_rate->set_selected (false);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_selection_changed) {
|
||||
selection_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_sample_format (SampleFormatPtr const & format)
|
||||
{
|
||||
|
||||
bool do_selection_changed = !pending_selection_change;
|
||||
if (!pending_selection_change) {
|
||||
pending_selection_change = true;
|
||||
}
|
||||
|
||||
if (format) {
|
||||
current_selection->set_sample_format (format->format);
|
||||
} else {
|
||||
current_selection->set_sample_format (ExportFormatBase::SF_None);
|
||||
|
||||
SampleFormatPtr current_format = get_selected_sample_format();
|
||||
if (current_format) {
|
||||
current_format->set_selected (false);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_selection_changed) {
|
||||
selection_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_dither_type (DitherTypePtr const & type)
|
||||
{
|
||||
|
||||
bool do_selection_changed = !pending_selection_change;
|
||||
if (!pending_selection_change) {
|
||||
pending_selection_change = true;
|
||||
}
|
||||
|
||||
if (type) {
|
||||
current_selection->set_dither_type (type->type);
|
||||
} else {
|
||||
current_selection->set_dither_type (ExportFormatBase::D_None);
|
||||
}
|
||||
|
||||
if (do_selection_changed) {
|
||||
selection_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::selection_changed ()
|
||||
{
|
||||
/* Get a list of incompatible compatibility selections */
|
||||
|
||||
CompatList incompatibles;
|
||||
for (CompatList::iterator it = compatibilities.begin(); it != compatibilities.end(); ++it) {
|
||||
if (!current_selection->is_compatible_with (**it)) {
|
||||
incompatibles.push_back (*it);
|
||||
}
|
||||
}
|
||||
|
||||
/* Deselect them */
|
||||
|
||||
for (CompatList::iterator it = incompatibles.begin(); it != incompatibles.end(); ++it) {
|
||||
(*it)->set_selected (false);
|
||||
}
|
||||
|
||||
/* Mark compatibility for everything necessary */
|
||||
|
||||
std::set<ExportFormatBase::Quality> compatible_qualities;
|
||||
FormatBasePtr compat_intersect = get_compatibility_intersection ();
|
||||
ExportFormatCompatibility global_compat (*compat_intersect);
|
||||
|
||||
for (FormatList::iterator it = formats.begin(); it != formats.end(); ++it) {
|
||||
if ((*it)->set_compatibility_state (global_compat)) {
|
||||
compatible_qualities.insert ((*it)->get_quality());
|
||||
}
|
||||
}
|
||||
|
||||
bool any_quality_compatible = true;
|
||||
for (QualityList::iterator it = qualities.begin(); it != qualities.end(); ++it) {
|
||||
if (compatible_qualities.find((*it)->quality) != compatible_qualities.end()) {
|
||||
(*it)->set_compatible (true);
|
||||
|
||||
} else {
|
||||
(*it)->set_compatible (false);
|
||||
|
||||
if ((*it)->quality != ExportFormatBase::Q_Any) {
|
||||
any_quality_compatible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (any_quality_compatible) {
|
||||
for (QualityList::iterator it = qualities.begin(); it != qualities.end(); ++it) {
|
||||
if ((*it)->quality == ExportFormatBase::Q_Any) {
|
||||
(*it)->set_compatible (true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (SampleRateList::iterator it = sample_rates.begin(); it != sample_rates.end(); ++it) {
|
||||
if (compat_intersect->has_sample_rate ((*it)->rate)) {
|
||||
(*it)->set_compatible (true);
|
||||
} else {
|
||||
(*it)->set_compatible (false);
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<HasSampleFormat> hsf;
|
||||
if (hsf = boost::dynamic_pointer_cast<HasSampleFormat> (get_selected_format())) {
|
||||
|
||||
SampleFormatList sf_list = hsf->get_sample_formats();
|
||||
for (SampleFormatList::iterator it = sf_list.begin(); it != sf_list.end(); ++it) {
|
||||
if (compat_intersect->has_sample_format ((*it)->format)) {
|
||||
(*it)->set_compatible (true);
|
||||
} else {
|
||||
(*it)->set_compatible (false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Signal completeness */
|
||||
|
||||
CompleteChanged (current_selection->is_complete());
|
||||
|
||||
/* Reset pending state */
|
||||
|
||||
pending_selection_change = false;
|
||||
}
|
||||
|
||||
ExportFormatManager::QualityPtr
|
||||
ExportFormatManager::get_selected_quality ()
|
||||
{
|
||||
for (QualityList::iterator it = qualities.begin(); it != qualities.end(); ++it) {
|
||||
if ((*it)->selected()) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
|
||||
return QualityPtr();
|
||||
}
|
||||
|
||||
ExportFormatManager::FormatPtr
|
||||
ExportFormatManager::get_selected_format ()
|
||||
{
|
||||
FormatPtr format;
|
||||
|
||||
for (FormatList::iterator it = formats.begin(); it != formats.end(); ++it) {
|
||||
if ((*it)->selected()) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
ExportFormatManager::SampleRatePtr
|
||||
ExportFormatManager::get_selected_sample_rate ()
|
||||
{
|
||||
for (SampleRateList::iterator it = sample_rates.begin(); it != sample_rates.end(); ++it) {
|
||||
if ((*it)->selected()) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
|
||||
return SampleRatePtr();
|
||||
}
|
||||
|
||||
ExportFormatManager::SampleFormatPtr
|
||||
ExportFormatManager::get_selected_sample_format ()
|
||||
{
|
||||
boost::shared_ptr<HasSampleFormat> hsf;
|
||||
|
||||
if (hsf = boost::dynamic_pointer_cast<HasSampleFormat> (get_selected_format())) {
|
||||
return hsf->get_selected_sample_format ();
|
||||
} else {
|
||||
return SampleFormatPtr ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ExportFormatManager::FormatBasePtr
|
||||
ExportFormatManager::get_compatibility_intersection ()
|
||||
{
|
||||
FormatBasePtr compat_intersect = universal_set;
|
||||
|
||||
for (CompatList::iterator it = compatibilities.begin(); it != compatibilities.end(); ++it) {
|
||||
if ((*it)->selected ()) {
|
||||
compat_intersect = compat_intersect->get_intersection (**it);
|
||||
}
|
||||
}
|
||||
|
||||
return compat_intersect;
|
||||
}
|
||||
|
||||
}; // namespace ARDOUR
|
||||
602
libs/ardour/export_format_specification.cc
Normal file
602
libs/ardour/export_format_specification.cc
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/export_format_specification.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <ardour/export_format_compatibility.h>
|
||||
#include <ardour/export_formats.h>
|
||||
#include <ardour/session.h>
|
||||
|
||||
#include <pbd/error.h>
|
||||
#include <pbd/xml++.h>
|
||||
#include <pbd/enumwriter.h>
|
||||
#include <pbd/convert.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
using namespace PBD;
|
||||
using std::string;
|
||||
|
||||
/* The id counter is initialized to 1000 so that user created profiles have a id > 1000
|
||||
* while ones shipped with ardour have one < 1000
|
||||
*/
|
||||
uint32_t ExportFormatSpecification::_counter = 1000;
|
||||
|
||||
ExportFormatSpecification::Time &
|
||||
ExportFormatSpecification::Time::operator= (AnyTime const & other)
|
||||
{
|
||||
type = other.type;
|
||||
smpte = other.smpte;
|
||||
bbt = other.bbt;
|
||||
|
||||
if (type == Frames) {
|
||||
frames = other.frames;
|
||||
} else {
|
||||
seconds = other.seconds;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
nframes_t
|
||||
ExportFormatSpecification::Time::get_frames (nframes_t target_rate) const
|
||||
{
|
||||
//TODO position
|
||||
nframes_t duration = session.convert_to_frames_at (0, *this);
|
||||
|
||||
return ((double) target_rate / session.frame_rate()) * duration + 0.5;
|
||||
}
|
||||
|
||||
XMLNode &
|
||||
ExportFormatSpecification::Time::get_state ()
|
||||
{
|
||||
|
||||
XMLNode * node = new XMLNode ("Duration");
|
||||
|
||||
node->add_property ("format", enum_2_string (type));
|
||||
|
||||
switch (type) {
|
||||
case SMPTE:
|
||||
node->add_property ("hours", to_string (smpte.hours, std::dec));
|
||||
node->add_property ("minutes", to_string (smpte.minutes, std::dec));
|
||||
node->add_property ("seconds", to_string (smpte.seconds, std::dec));
|
||||
node->add_property ("frames", to_string (smpte.frames, std::dec));
|
||||
break;
|
||||
case BBT:
|
||||
node->add_property ("bars", to_string (bbt.bars, std::dec));
|
||||
node->add_property ("beats", to_string (bbt.beats, std::dec));
|
||||
node->add_property ("ticks", to_string (bbt.ticks, std::dec));
|
||||
break;
|
||||
case Frames:
|
||||
node->add_property ("frames", to_string (frames, std::dec));
|
||||
break;
|
||||
case Seconds:
|
||||
node->add_property ("seconds", to_string (seconds, std::dec));
|
||||
break;
|
||||
}
|
||||
|
||||
return *node;
|
||||
}
|
||||
|
||||
int
|
||||
ExportFormatSpecification::Time::set_state (const XMLNode & node)
|
||||
{
|
||||
XMLProperty const * prop;
|
||||
|
||||
prop = node.property ("format");
|
||||
|
||||
if (!prop) { return -1; }
|
||||
|
||||
type = (Type) string_2_enum (prop->value(), Type);
|
||||
|
||||
switch (type) {
|
||||
case SMPTE:
|
||||
if ((prop = node.property ("hours"))) {
|
||||
smpte.hours = atoi (prop->value());
|
||||
}
|
||||
|
||||
if ((prop = node.property ("minutes"))) {
|
||||
smpte.minutes = atoi (prop->value());
|
||||
}
|
||||
|
||||
if ((prop = node.property ("seconds"))) {
|
||||
smpte.seconds = atoi (prop->value());
|
||||
}
|
||||
|
||||
if ((prop = node.property ("frames"))) {
|
||||
smpte.frames = atoi (prop->value());
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BBT:
|
||||
if ((prop = node.property ("bars"))) {
|
||||
bbt.bars = atoi (prop->value());
|
||||
}
|
||||
|
||||
if ((prop = node.property ("beats"))) {
|
||||
bbt.beats = atoi (prop->value());
|
||||
}
|
||||
|
||||
if ((prop = node.property ("ticks"))) {
|
||||
bbt.ticks = atoi (prop->value());
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Frames:
|
||||
if ((prop = node.property ("frames"))) {
|
||||
std::istringstream iss (prop->value());
|
||||
iss >> frames;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Seconds:
|
||||
if ((prop = node.property ("seconds"))) {
|
||||
seconds = atof (prop->value());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ExportFormatSpecification::ExportFormatSpecification (Session & s) :
|
||||
session (s),
|
||||
|
||||
has_sample_format (false),
|
||||
supports_tagging (false),
|
||||
_has_broadcast_info (false),
|
||||
_channel_limit (0),
|
||||
_dither_type (D_None),
|
||||
_src_quality (SRC_SincBest),
|
||||
_tag (true),
|
||||
|
||||
_trim_beginning (false),
|
||||
_silence_beginning (s),
|
||||
_trim_end (false),
|
||||
_silence_end (s),
|
||||
|
||||
_normalize (false),
|
||||
_normalize_target (1.0)
|
||||
{
|
||||
format_ids.insert (F_None);
|
||||
endiannesses.insert (E_FileDefault);
|
||||
sample_formats.insert (SF_None);
|
||||
sample_rates.insert (SR_None);
|
||||
qualities.insert (Q_None);
|
||||
|
||||
_id = ++_counter;
|
||||
}
|
||||
|
||||
ExportFormatSpecification::ExportFormatSpecification (Session & s, XMLNode const & state) :
|
||||
session (s),
|
||||
_silence_beginning (s),
|
||||
_silence_end (s)
|
||||
{
|
||||
_silence_beginning.type = Time::SMPTE;
|
||||
_silence_end.type = Time::SMPTE;
|
||||
|
||||
set_state (state);
|
||||
}
|
||||
|
||||
ExportFormatSpecification::ExportFormatSpecification (ExportFormatSpecification const & other) :
|
||||
session (other.session),
|
||||
_silence_beginning (other.session),
|
||||
_silence_end (other.session)
|
||||
{
|
||||
set_name (other.name() + " (copy)");
|
||||
_id = ++_counter;
|
||||
|
||||
_format_name = other._format_name;
|
||||
has_sample_format = other.has_sample_format;
|
||||
|
||||
supports_tagging = other.supports_tagging;
|
||||
_has_broadcast_info = other._has_broadcast_info;
|
||||
_channel_limit = other._channel_limit;
|
||||
|
||||
set_type (other.type());
|
||||
set_format_id (other.format_id());
|
||||
set_endianness (other.endianness());
|
||||
set_sample_format (other.sample_format());
|
||||
set_sample_rate (other.sample_rate());
|
||||
set_quality (other.quality());
|
||||
|
||||
set_dither_type (other.dither_type());
|
||||
set_src_quality (other.src_quality());
|
||||
set_trim_beginning (other.trim_beginning());
|
||||
set_trim_end (other.trim_end());
|
||||
set_normalize (other.normalize());
|
||||
set_normalize_target (other.normalize_target());
|
||||
|
||||
set_tag (other.tag());
|
||||
|
||||
set_silence_beginning (other.silence_beginning_time());
|
||||
set_silence_end (other.silence_end_time());
|
||||
}
|
||||
|
||||
ExportFormatSpecification::~ExportFormatSpecification ()
|
||||
{
|
||||
}
|
||||
|
||||
XMLNode &
|
||||
ExportFormatSpecification::get_state ()
|
||||
{
|
||||
XMLNode * node;
|
||||
XMLNode * root = new XMLNode ("ExportFormatSpecification");
|
||||
|
||||
root->add_property ("name", _name);
|
||||
root->add_property ("id", to_string (_id, std::dec));
|
||||
|
||||
node = root->add_child ("Encoding");
|
||||
node->add_property ("id", enum_2_string (format_id()));
|
||||
node->add_property ("type", enum_2_string (type()));
|
||||
node->add_property ("extension", extension());
|
||||
node->add_property ("name", _format_name);
|
||||
node->add_property ("has-sample-format", has_sample_format ? "true" : "false");
|
||||
node->add_property ("channel-limit", to_string (_channel_limit, std::dec));
|
||||
|
||||
node = root->add_child ("SampleRate");
|
||||
node->add_property ("rate", to_string (sample_rate(), std::dec));
|
||||
|
||||
node = root->add_child ("SRCQuality");
|
||||
node->add_property ("quality", enum_2_string (src_quality()));
|
||||
|
||||
XMLNode * enc_opts = root->add_child ("EncodingOptions");
|
||||
|
||||
add_option (enc_opts, "sample-format", enum_2_string (sample_format()));
|
||||
add_option (enc_opts, "dithering", enum_2_string (dither_type()));
|
||||
add_option (enc_opts, "tag-metadata", _tag ? "true" : "false");
|
||||
add_option (enc_opts, "tag-support", supports_tagging ? "true" : "false");
|
||||
add_option (enc_opts, "broadcast-info", _has_broadcast_info ? "true" : "false");
|
||||
|
||||
XMLNode * processing = root->add_child ("Processing");
|
||||
|
||||
node = processing->add_child ("Normalize");
|
||||
node->add_property ("enabled", normalize() ? "true" : "false");
|
||||
node->add_property ("target", to_string (normalize_target(), std::dec));
|
||||
|
||||
XMLNode * silence = processing->add_child ("Silence");
|
||||
XMLNode * start = silence->add_child ("Start");
|
||||
XMLNode * end = silence->add_child ("End");
|
||||
|
||||
node = start->add_child ("Trim");
|
||||
node->add_property ("enabled", trim_beginning() ? "true" : "false");
|
||||
|
||||
node = start->add_child ("Add");
|
||||
node->add_property ("enabled", silence_beginning() > 0 ? "true" : "false");
|
||||
node->add_child_nocopy (_silence_beginning.get_state());
|
||||
|
||||
node = end->add_child ("Trim");
|
||||
node->add_property ("enabled", trim_end() ? "true" : "false");
|
||||
|
||||
node = end->add_child ("Add");
|
||||
node->add_property ("enabled", silence_end() > 0 ? "true" : "false");
|
||||
node->add_child_nocopy (_silence_end.get_state());
|
||||
|
||||
return *root;
|
||||
}
|
||||
|
||||
int
|
||||
ExportFormatSpecification::set_state (const XMLNode & root)
|
||||
{
|
||||
XMLProperty const * prop;
|
||||
XMLNode const * child;
|
||||
string value;
|
||||
|
||||
if ((prop = root.property ("name"))) {
|
||||
_name = prop->value();
|
||||
}
|
||||
|
||||
if ((prop = root.property ("id"))) {
|
||||
std::istringstream iss (prop->value());
|
||||
iss >> _id;
|
||||
}
|
||||
|
||||
/* Encoding and SRC */
|
||||
|
||||
if ((child = root.child ("Encoding"))) {
|
||||
if ((prop = child->property ("id"))) {
|
||||
set_format_id ((FormatId) string_2_enum (prop->value(), FormatId));
|
||||
}
|
||||
|
||||
if ((prop = child->property ("type"))) {
|
||||
set_type ((Type) string_2_enum (prop->value(), Type));
|
||||
}
|
||||
|
||||
if ((prop = child->property ("extension"))) {
|
||||
set_extension (prop->value());
|
||||
}
|
||||
|
||||
if ((prop = child->property ("name"))) {
|
||||
_format_name = prop->value();
|
||||
}
|
||||
|
||||
if ((prop = child->property ("has-sample-format"))) {
|
||||
has_sample_format = !prop->value().compare ("true");
|
||||
}
|
||||
|
||||
if ((prop = child->property ("channel-limit"))) {
|
||||
_channel_limit = atoi (prop->value());
|
||||
}
|
||||
}
|
||||
|
||||
if ((child = root.child ("SampleRate"))) {
|
||||
if ((prop = child->property ("rate"))) {
|
||||
set_sample_rate ( (SampleRate) string_2_enum (prop->value(), SampleRate));
|
||||
}
|
||||
}
|
||||
|
||||
if ((child = root.child ("SRCQuality"))) {
|
||||
if ((prop = child->property ("quality"))) {
|
||||
_src_quality = (SRCQuality) string_2_enum (prop->value(), SRCQuality);
|
||||
}
|
||||
}
|
||||
|
||||
/* Encoding options */
|
||||
|
||||
if ((child = root.child ("EncodingOptions"))) {
|
||||
set_sample_format ((SampleFormat) string_2_enum (get_option (child, "sample-format"), SampleFormat));
|
||||
set_dither_type ((DitherType) string_2_enum (get_option (child, "dithering"), DitherType));
|
||||
set_tag (!(get_option (child, "tag-metadata").compare ("true")));
|
||||
supports_tagging = (!(get_option (child, "tag-support").compare ("true")));
|
||||
_has_broadcast_info = (!(get_option (child, "broadcast-info").compare ("true")));
|
||||
}
|
||||
|
||||
/* Processing */
|
||||
|
||||
XMLNode const * proc = root.child ("Processing");
|
||||
if (!proc) { std::cerr << X_("Could not load processing for export format") << std::endl; return -1; }
|
||||
|
||||
if ((child = proc->child ("Normalize"))) {
|
||||
if ((prop = child->property ("enabled"))) {
|
||||
_normalize = (!prop->value().compare ("true"));
|
||||
}
|
||||
|
||||
if ((prop = child->property ("target"))) {
|
||||
_normalize_target = atof (prop->value());
|
||||
}
|
||||
}
|
||||
|
||||
XMLNode const * silence = proc->child ("Silence");
|
||||
if (!silence) { std::cerr << X_("Could not load silence for export format") << std::endl; return -1; }
|
||||
|
||||
XMLNode const * start = silence->child ("Start");
|
||||
XMLNode const * end = silence->child ("End");
|
||||
if (!start || !end) { std::cerr << X_("Could not load end or start silence for export format") << std::endl; return -1; }
|
||||
|
||||
/* Silence start */
|
||||
|
||||
if ((child = start->child ("Trim"))) {
|
||||
if ((prop = child->property ("enabled"))) {
|
||||
_trim_beginning = (!prop->value().compare ("true"));
|
||||
}
|
||||
}
|
||||
|
||||
if ((child = start->child ("Add"))) {
|
||||
if ((prop = child->property ("enabled"))) {
|
||||
if (!prop->value().compare ("true")) {
|
||||
if ((child = child->child ("Duration"))) {
|
||||
_silence_beginning.set_state (*child);
|
||||
}
|
||||
} else {
|
||||
_silence_beginning.type = Time::SMPTE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Silence end */
|
||||
|
||||
if ((child = end->child ("Trim"))) {
|
||||
if ((prop = child->property ("enabled"))) {
|
||||
_trim_end = (!prop->value().compare ("true"));
|
||||
}
|
||||
}
|
||||
|
||||
if ((child = end->child ("Add"))) {
|
||||
if ((prop = child->property ("enabled"))) {
|
||||
if (!prop->value().compare ("true")) {
|
||||
if ((child = child->child ("Duration"))) {
|
||||
_silence_end.set_state (*child);
|
||||
}
|
||||
} else {
|
||||
_silence_end.type = Time::SMPTE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
ExportFormatSpecification::is_compatible_with (ExportFormatCompatibility const & compatibility) const
|
||||
{
|
||||
boost::shared_ptr<ExportFormatBase> intersection = get_intersection (compatibility);
|
||||
|
||||
if (intersection->formats_empty() && format_id() != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (intersection->endiannesses_empty() && endianness() != E_FileDefault) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (intersection->sample_rates_empty() && sample_rate() != SR_None) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (intersection->sample_formats_empty() && sample_format() != SF_None) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (intersection->qualities_empty() && quality() != Q_None) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ExportFormatSpecification::is_complete () const
|
||||
{
|
||||
if (type() == T_None) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!format_id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sample_rate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (has_sample_format) {
|
||||
if (sample_format() == SF_None) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatSpecification::set_format (boost::shared_ptr<ExportFormat> format)
|
||||
{
|
||||
if (format) {
|
||||
set_format_id (format->get_format_id ());
|
||||
set_type (format->get_type());
|
||||
set_extension (format->extension());
|
||||
|
||||
if (format->get_explicit_sample_format()) {
|
||||
set_sample_format (format->get_explicit_sample_format());
|
||||
}
|
||||
|
||||
if (format->has_sample_format()) {
|
||||
has_sample_format = true;
|
||||
}
|
||||
|
||||
if (format->has_broadcast_info()) {
|
||||
_has_broadcast_info = true;
|
||||
}
|
||||
|
||||
supports_tagging = format->supports_tagging ();
|
||||
_channel_limit = format->get_channel_limit();
|
||||
|
||||
_format_name = format->name();
|
||||
} else {
|
||||
set_format_id (F_None);
|
||||
set_type (T_None);
|
||||
set_extension ("");
|
||||
_has_broadcast_info = false;
|
||||
has_sample_format = false;
|
||||
supports_tagging = false;
|
||||
_channel_limit = 0;
|
||||
_format_name = "";
|
||||
}
|
||||
}
|
||||
|
||||
Glib::ustring
|
||||
ExportFormatSpecification::description ()
|
||||
{
|
||||
Glib::ustring desc;
|
||||
|
||||
desc = _name + ": ";
|
||||
|
||||
if (_normalize) {
|
||||
desc += _("normalize, ");
|
||||
}
|
||||
|
||||
if (_trim_beginning && _trim_end) {
|
||||
desc += _("trim, ");
|
||||
} else if (_trim_beginning) {
|
||||
desc += _("trim start, ");
|
||||
} else if (_trim_end) {
|
||||
desc += "trim end, ";
|
||||
}
|
||||
|
||||
desc += _format_name + ", ";
|
||||
|
||||
if (has_sample_format) {
|
||||
desc += HasSampleFormat::get_sample_format_name (sample_format()) + ", ";
|
||||
}
|
||||
|
||||
switch (sample_rate()) {
|
||||
case SR_22_05:
|
||||
desc += "22,5 kHz";
|
||||
break;
|
||||
case SR_44_1:
|
||||
desc += "44,1 kHz";
|
||||
break;
|
||||
case SR_48:
|
||||
desc += "48 kHz";
|
||||
break;
|
||||
case SR_88_2:
|
||||
desc += "88,2 kHz";
|
||||
break;
|
||||
case SR_96:
|
||||
desc += "96 kHz";
|
||||
break;
|
||||
case SR_192:
|
||||
desc += "192 kHz";
|
||||
break;
|
||||
case SR_None:
|
||||
break;
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatSpecification::add_option (XMLNode * node, std::string const & name, std::string const & value)
|
||||
{
|
||||
node = node->add_child ("Option");
|
||||
node->add_property ("name", name);
|
||||
node->add_property ("value", value);
|
||||
}
|
||||
|
||||
std::string
|
||||
ExportFormatSpecification::get_option (XMLNode const * node, std::string const & name)
|
||||
{
|
||||
XMLNodeList list (node->children ("Option"));
|
||||
|
||||
for (XMLNodeList::iterator it = list.begin(); it != list.end(); ++it) {
|
||||
XMLProperty * prop = (*it)->property ("name");
|
||||
if (prop && !name.compare (prop->value())) {
|
||||
prop = (*it)->property ("value");
|
||||
if (prop) {
|
||||
return prop->value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "Could not load encoding option \"" << name << "\" for export format" << std::endl;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
}; // namespace ARDOUR
|
||||
331
libs/ardour/export_formats.cc
Normal file
331
libs/ardour/export_formats.cc
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/export_formats.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
bool
|
||||
ExportFormat::has_sample_format ()
|
||||
{
|
||||
return dynamic_cast<HasSampleFormat *> (this);
|
||||
}
|
||||
|
||||
bool
|
||||
ExportFormat::sample_format_is_compatible (SampleFormat format) const
|
||||
{
|
||||
return (sample_formats.find (format) != sample_formats.end());
|
||||
}
|
||||
|
||||
/*** HasSampleFormat ***/
|
||||
|
||||
HasSampleFormat::HasSampleFormat (ExportFormatBase::SampleFormatSet & sample_formats) :
|
||||
_sample_formats (sample_formats)
|
||||
{
|
||||
/* Dither Types */
|
||||
|
||||
add_dither_type (ExportFormatBase::D_Shaped, _("Shaped Noise"));
|
||||
add_dither_type (ExportFormatBase::D_Tri, _("Triangular"));
|
||||
add_dither_type (ExportFormatBase::D_Rect, _("Rectangular"));
|
||||
add_dither_type (ExportFormatBase::D_None, _("None"));
|
||||
}
|
||||
|
||||
void
|
||||
HasSampleFormat::add_sample_format (ExportFormatBase::SampleFormat format)
|
||||
{
|
||||
_sample_formats.insert (format);
|
||||
|
||||
SampleFormatPtr ptr (new SampleFormatState (format, get_sample_format_name (format)));
|
||||
sample_format_states.push_back (ptr);
|
||||
ptr->SelectChanged.connect (sigc::bind (SampleFormatSelectChanged.make_slot(), WeakSampleFormatPtr (ptr)));
|
||||
ptr->SelectChanged.connect (sigc::mem_fun (*this, &HasSampleFormat::update_sample_format_selection));
|
||||
ptr->CompatibleChanged.connect (sigc::bind (SampleFormatCompatibleChanged.make_slot(), WeakSampleFormatPtr (ptr)));
|
||||
}
|
||||
|
||||
void
|
||||
HasSampleFormat::add_dither_type (ExportFormatBase::DitherType type, Glib::ustring name)
|
||||
{
|
||||
DitherTypePtr ptr (new DitherTypeState (type, name));
|
||||
dither_type_states.push_back (ptr);
|
||||
ptr->SelectChanged.connect (sigc::bind (DitherTypeSelectChanged.make_slot(), WeakDitherTypePtr (ptr)));
|
||||
ptr->SelectChanged.connect (sigc::mem_fun (*this, &HasSampleFormat::update_dither_type_selection));
|
||||
ptr->CompatibleChanged.connect (sigc::bind (DitherTypeCompatibleChanged.make_slot(), WeakDitherTypePtr (ptr)));
|
||||
}
|
||||
|
||||
HasSampleFormat::SampleFormatPtr
|
||||
HasSampleFormat::get_selected_sample_format ()
|
||||
{
|
||||
for (SampleFormatList::iterator it = sample_format_states.begin(); it != sample_format_states.end(); ++it) {
|
||||
if ((*it)->selected()) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
|
||||
return SampleFormatPtr();
|
||||
}
|
||||
|
||||
HasSampleFormat::DitherTypePtr
|
||||
HasSampleFormat::get_selected_dither_type ()
|
||||
{
|
||||
for (DitherTypeList::iterator it = dither_type_states.begin(); it != dither_type_states.end(); ++it) {
|
||||
if ((*it)->selected()) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
|
||||
return DitherTypePtr();
|
||||
}
|
||||
|
||||
void
|
||||
HasSampleFormat::update_sample_format_selection (bool)
|
||||
{
|
||||
SampleFormatPtr format = get_selected_sample_format();
|
||||
if (!format) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (format->format == ExportFormatBase::SF_24 ||
|
||||
format->format == ExportFormatBase::SF_32 ||
|
||||
format->format == ExportFormatBase::SF_Float ||
|
||||
format->format == ExportFormatBase::SF_Double) {
|
||||
for (DitherTypeList::iterator it = dither_type_states.begin(); it != dither_type_states.end(); ++it) {
|
||||
if ((*it)->type == ExportFormatBase::D_None) {
|
||||
(*it)->set_selected (true);
|
||||
} else {
|
||||
(*it)->set_compatible (false);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
for (DitherTypeList::iterator it = dither_type_states.begin(); it != dither_type_states.end(); ++it) {
|
||||
(*it)->set_compatible (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HasSampleFormat::update_dither_type_selection (bool)
|
||||
{
|
||||
DitherTypePtr type = get_selected_dither_type();
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!type->compatible()) {
|
||||
SampleFormatPtr format = get_selected_sample_format();
|
||||
if (format) {
|
||||
format->set_selected (false);
|
||||
}
|
||||
|
||||
for (DitherTypeList::iterator it = dither_type_states.begin(); it != dither_type_states.end(); ++it) {
|
||||
(*it)->set_compatible (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string
|
||||
HasSampleFormat::get_sample_format_name (ExportFormatBase::SampleFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case ExportFormatBase::SF_8:
|
||||
return _("8bit");
|
||||
case ExportFormatBase::SF_16:
|
||||
return _("16bit");
|
||||
case ExportFormatBase::SF_24:
|
||||
return _("24bit");
|
||||
case ExportFormatBase::SF_32:
|
||||
return _("32bit");
|
||||
case ExportFormatBase::SF_Float:
|
||||
return _("float");
|
||||
case ExportFormatBase::SF_Double:
|
||||
return _("double");
|
||||
case ExportFormatBase::SF_U8:
|
||||
return _("8bit unsigned");
|
||||
case ExportFormatBase::SF_Vorbis:
|
||||
return _("Vorbis sample format");
|
||||
case ExportFormatBase::SF_None:
|
||||
return _("No sample format");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/*** Linear ***/
|
||||
|
||||
ExportFormatLinear::ExportFormatLinear (Glib::ustring name, FormatId format_id) :
|
||||
HasSampleFormat (sample_formats),
|
||||
_default_sample_format (SF_None)
|
||||
{
|
||||
set_name (name);
|
||||
set_format_id (format_id);
|
||||
|
||||
add_sample_rate (SR_22_05);
|
||||
add_sample_rate (SR_44_1);
|
||||
add_sample_rate (SR_48);
|
||||
add_sample_rate (SR_88_2);
|
||||
add_sample_rate (SR_96);
|
||||
add_sample_rate (SR_192);
|
||||
|
||||
add_endianness (E_FileDefault);
|
||||
|
||||
set_quality (Q_LosslessLinear);
|
||||
}
|
||||
|
||||
bool
|
||||
ExportFormatLinear::set_compatibility_state (ExportFormatCompatibility const & compatibility)
|
||||
{
|
||||
/* Global state */
|
||||
|
||||
bool compatible = true;
|
||||
|
||||
if (!compatibility.has_quality (Q_LosslessLinear)) {
|
||||
compatible = false;
|
||||
}
|
||||
|
||||
if (!compatibility.has_format (get_format_id())) {
|
||||
compatible = false;
|
||||
}
|
||||
|
||||
boost::shared_ptr<ExportFormatBase> intersection = get_intersection (compatibility);
|
||||
|
||||
if (intersection->endiannesses_empty()) {
|
||||
compatible = false;
|
||||
}
|
||||
|
||||
if (intersection->sample_rates_empty()) {
|
||||
compatible = false;
|
||||
}
|
||||
|
||||
if (intersection->sample_formats_empty()) {
|
||||
compatible = false;
|
||||
}
|
||||
|
||||
set_compatible (compatible);
|
||||
|
||||
/* Sample Formats */
|
||||
|
||||
for (SampleFormatList::iterator it = sample_format_states.begin(); it != sample_format_states.end(); ++it) {
|
||||
(*it)->set_compatible (compatibility.has_sample_format ((*it)->format));
|
||||
}
|
||||
|
||||
return compatible;
|
||||
}
|
||||
|
||||
/*** Ogg Vorbis ***/
|
||||
|
||||
ExportFormatOggVorbis::ExportFormatOggVorbis ()
|
||||
{
|
||||
set_name ("Ogg Vorbis");
|
||||
set_format_id (F_Ogg);
|
||||
sample_formats.insert (SF_Vorbis);
|
||||
|
||||
add_sample_rate (SR_22_05);
|
||||
add_sample_rate (SR_44_1);
|
||||
add_sample_rate (SR_48);
|
||||
add_sample_rate (SR_88_2);
|
||||
add_sample_rate (SR_96);
|
||||
add_sample_rate (SR_192);
|
||||
|
||||
add_endianness (E_FileDefault);
|
||||
|
||||
set_extension ("ogg");
|
||||
set_quality (Q_LossyCompression);
|
||||
}
|
||||
|
||||
bool
|
||||
ExportFormatOggVorbis::set_compatibility_state (ExportFormatCompatibility const & compatibility)
|
||||
{
|
||||
bool compatible = compatibility.has_format (F_Ogg);
|
||||
set_compatible (compatible);
|
||||
return compatible;
|
||||
}
|
||||
|
||||
/*** FLAC ***/
|
||||
|
||||
ExportFormatFLAC::ExportFormatFLAC () :
|
||||
HasSampleFormat (sample_formats)
|
||||
{
|
||||
set_name ("FLAC");
|
||||
set_format_id (F_FLAC);
|
||||
|
||||
add_sample_rate (SR_22_05);
|
||||
add_sample_rate (SR_44_1);
|
||||
add_sample_rate (SR_48);
|
||||
add_sample_rate (SR_88_2);
|
||||
add_sample_rate (SR_96);
|
||||
add_sample_rate (SR_192);
|
||||
|
||||
add_sample_format (SF_8);
|
||||
add_sample_format (SF_16);
|
||||
add_sample_format (SF_24);
|
||||
|
||||
add_endianness (E_FileDefault);
|
||||
|
||||
set_extension ("flac");
|
||||
set_quality (Q_LosslessCompression);
|
||||
}
|
||||
|
||||
bool
|
||||
ExportFormatFLAC::set_compatibility_state (ExportFormatCompatibility const & compatibility)
|
||||
{
|
||||
bool compatible = compatibility.has_format (F_FLAC);
|
||||
set_compatible (compatible);
|
||||
return compatible;
|
||||
}
|
||||
|
||||
/*** BWF ***/
|
||||
|
||||
ExportFormatBWF::ExportFormatBWF () :
|
||||
HasSampleFormat (sample_formats)
|
||||
{
|
||||
set_name ("BWF");
|
||||
set_format_id (F_WAV);
|
||||
|
||||
add_sample_rate (SR_22_05);
|
||||
add_sample_rate (SR_44_1);
|
||||
add_sample_rate (SR_48);
|
||||
add_sample_rate (SR_88_2);
|
||||
add_sample_rate (SR_96);
|
||||
add_sample_rate (SR_192);
|
||||
|
||||
add_sample_format (SF_U8);
|
||||
add_sample_format (SF_16);
|
||||
add_sample_format (SF_24);
|
||||
add_sample_format (SF_32);
|
||||
add_sample_format (SF_Float);
|
||||
add_sample_format (SF_Double);
|
||||
|
||||
add_endianness (E_FileDefault);
|
||||
|
||||
set_extension ("wav");
|
||||
set_quality (Q_LosslessLinear);
|
||||
}
|
||||
|
||||
bool
|
||||
ExportFormatBWF::set_compatibility_state (ExportFormatCompatibility const & compatibility)
|
||||
{
|
||||
bool compatible = compatibility.has_format (F_WAV);
|
||||
set_compatible (compatible);
|
||||
return compatible;
|
||||
}
|
||||
|
||||
}; // namespace ARDOUR
|
||||
552
libs/ardour/export_handler.cc
Normal file
552
libs/ardour/export_handler.cc
Normal file
|
|
@ -0,0 +1,552 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/export_handler.h>
|
||||
|
||||
#include <pbd/convert.h>
|
||||
#include <pbd/filesystem.h>
|
||||
|
||||
#include <ardour/ardour.h>
|
||||
#include <ardour/configuration.h>
|
||||
#include <ardour/export_timespan.h>
|
||||
#include <ardour/export_channel_configuration.h>
|
||||
#include <ardour/export_status.h>
|
||||
#include <ardour/export_format_specification.h>
|
||||
#include <ardour/export_filename.h>
|
||||
#include <ardour/export_processor.h>
|
||||
#include <ardour/export_failed.h>
|
||||
|
||||
using namespace PBD;
|
||||
using std::ofstream;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
/*** ExportElementFactory ***/
|
||||
|
||||
ExportElementFactory::ExportElementFactory (Session & session) :
|
||||
session (session)
|
||||
{
|
||||
XMLProperty * prop;
|
||||
XMLNode * instant_node = ARDOUR::Config->instant_xml ("ExportFormatSpecification");
|
||||
if (instant_node && (prop = instant_node->property ("id-counter"))) {
|
||||
ExportFormatSpecification::init_counter (atoi (prop->value()));
|
||||
}
|
||||
}
|
||||
|
||||
ExportElementFactory::~ExportElementFactory ()
|
||||
{
|
||||
XMLNode * instant_node = new XMLNode ("ExportFormatSpecification");
|
||||
instant_node->add_property ("id-counter", to_string (ExportFormatSpecification::counter(), std::dec));
|
||||
ARDOUR::Config->add_instant_xml (*instant_node);
|
||||
}
|
||||
|
||||
ExportElementFactory::TimespanPtr
|
||||
ExportElementFactory::add_timespan ()
|
||||
{
|
||||
return TimespanPtr (new ExportTimespan (session.export_status, session.frame_rate()));
|
||||
}
|
||||
|
||||
ExportElementFactory::ChannelConfigPtr
|
||||
ExportElementFactory::add_channel_config ()
|
||||
{
|
||||
return ChannelConfigPtr (new ExportChannelConfiguration (session.export_status));
|
||||
}
|
||||
|
||||
ExportElementFactory::FormatPtr
|
||||
ExportElementFactory::add_format ()
|
||||
{
|
||||
return FormatPtr (new ExportFormatSpecification (session));
|
||||
}
|
||||
|
||||
ExportElementFactory::FormatPtr
|
||||
ExportElementFactory::add_format (XMLNode const & state)
|
||||
{
|
||||
return FormatPtr (new ExportFormatSpecification (session, state));
|
||||
}
|
||||
|
||||
ExportElementFactory::FormatPtr
|
||||
ExportElementFactory::add_format_copy (FormatPtr other)
|
||||
{
|
||||
return FormatPtr (new ExportFormatSpecification (*other));
|
||||
}
|
||||
|
||||
ExportElementFactory::FilenamePtr
|
||||
ExportElementFactory::add_filename ()
|
||||
{
|
||||
return FilenamePtr (new ExportFilename (session));
|
||||
}
|
||||
|
||||
ExportElementFactory::FilenamePtr
|
||||
ExportElementFactory::add_filename_copy (FilenamePtr other)
|
||||
{
|
||||
return FilenamePtr (new ExportFilename (*other));
|
||||
}
|
||||
|
||||
/*** ExportHandler ***/
|
||||
|
||||
ExportHandler::ExportHandler (Session & session) :
|
||||
ExportElementFactory (session),
|
||||
session (session),
|
||||
realtime (false)
|
||||
{
|
||||
processor.reset (new ExportProcessor (session));
|
||||
|
||||
files_written_connection = ExportProcessor::WritingFile.connect (sigc::mem_fun (files_written, &std::list<Glib::ustring>::push_back));
|
||||
}
|
||||
|
||||
ExportHandler::~ExportHandler ()
|
||||
{
|
||||
if (session.export_status.aborted()) {
|
||||
for (std::list<Glib::ustring>::iterator it = files_written.begin(); it != files_written.end(); ++it) {
|
||||
sys::remove (sys::path (*it));
|
||||
}
|
||||
}
|
||||
|
||||
channel_config_connection.disconnect();
|
||||
files_written_connection.disconnect();
|
||||
}
|
||||
|
||||
bool
|
||||
ExportHandler::add_export_config (TimespanPtr timespan, ChannelConfigPtr channel_config, FormatPtr format, FilenamePtr filename)
|
||||
{
|
||||
FileSpec spec (channel_config, format, filename);
|
||||
ConfigPair pair (timespan, spec);
|
||||
config_map.insert (pair);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Starts exporting the registered configurations
|
||||
/** The following happens, when do_export is called:
|
||||
* 1. Session is prepared in do_export
|
||||
* 2. start_timespan is called, which then registers all necessary channel configs to a timespan
|
||||
* 3. The timespan reads each unique channel into a tempfile and calls Session::stop_export when the end is reached
|
||||
* 4. stop_export emits ExportFinished after stopping the transport, this ends up calling finish_timespan
|
||||
* 5. finish_timespan registers all the relevant formats and filenames to relevant channel configurations
|
||||
* 6. finish_timespan does a manual call to timespan_thread_finished, which gets the next channel configuration
|
||||
* for the current timespan, calling write_files for it
|
||||
* 7. write_files writes the actual export files, composing them from the individual channels from tempfiles and
|
||||
* emits FilesWritten when it is done, which ends up calling timespan_thread_finished
|
||||
* 8. Once all channel configs are written, a new timespan is started by calling start_timespan
|
||||
* 9. When all timespans are written the session is taken out of export.
|
||||
*/
|
||||
|
||||
void
|
||||
ExportHandler::do_export (bool rt)
|
||||
{
|
||||
/* Count timespans */
|
||||
|
||||
ExportStatus & status = session.export_status;
|
||||
status.init();
|
||||
std::set<TimespanPtr> timespan_set;
|
||||
for (ConfigMap::iterator it = config_map.begin(); it != config_map.end(); ++it) {
|
||||
timespan_set.insert (it->first);
|
||||
}
|
||||
status.total_timespans = timespan_set.size();
|
||||
|
||||
/* Start export */
|
||||
|
||||
realtime = rt;
|
||||
|
||||
session.ExportFinished.connect (sigc::mem_fun (*this, &ExportHandler::finish_timespan));
|
||||
session.pre_export ();
|
||||
start_timespan ();
|
||||
}
|
||||
|
||||
struct LocationSortByStart {
|
||||
bool operator() (Location *a, Location *b) {
|
||||
return a->start() < b->start();
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
ExportHandler::export_cd_marker_file (TimespanPtr timespan, FormatPtr file_format, std::string filename, CDMarkerFormat format)
|
||||
{
|
||||
string filepath;
|
||||
void (ExportHandler::*header_func) (CDMarkerStatus &);
|
||||
void (ExportHandler::*track_func) (CDMarkerStatus &);
|
||||
void (ExportHandler::*index_func) (CDMarkerStatus &);
|
||||
|
||||
switch (format) {
|
||||
case CDMarkerTOC:
|
||||
filepath = filename + ".toc";
|
||||
header_func = &ExportHandler::write_toc_header;
|
||||
track_func = &ExportHandler::write_track_info_toc;
|
||||
index_func = &ExportHandler::write_index_info_toc;
|
||||
break;
|
||||
case CDMarkerCUE:
|
||||
filepath = filename + ".cue";
|
||||
header_func = &ExportHandler::write_cue_header;
|
||||
track_func = &ExportHandler::write_track_info_cue;
|
||||
index_func = &ExportHandler::write_index_info_cue;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
CDMarkerStatus status (filepath, timespan, file_format, filename);
|
||||
|
||||
if (!status.out) {
|
||||
error << string_compose(_("Editor: cannot open \"%1\" as export file for CD marker file"), filepath) << endmsg;
|
||||
return;
|
||||
}
|
||||
|
||||
(this->*header_func) (status);
|
||||
|
||||
/* Get locations and sort */
|
||||
|
||||
Locations::LocationList const & locations (session.locations()->list());
|
||||
Locations::LocationList::const_iterator i;
|
||||
Locations::LocationList temp;
|
||||
|
||||
for (i = locations.begin(); i != locations.end(); ++i) {
|
||||
if ((*i)->start() >= timespan->get_start() && (*i)->end() <= timespan->get_end() && (*i)->is_cd_marker() && !(*i)->is_end()) {
|
||||
temp.push_back (*i);
|
||||
}
|
||||
}
|
||||
|
||||
if (temp.empty()) {
|
||||
// TODO One index marker for whole thing
|
||||
return;
|
||||
}
|
||||
|
||||
LocationSortByStart cmp;
|
||||
temp.sort (cmp);
|
||||
Locations::LocationList::const_iterator nexti;
|
||||
|
||||
/* Start actual marker stuff */
|
||||
|
||||
nframes_t last_end_time = timespan->get_start(), last_start_time = timespan->get_start();
|
||||
status.track_position = last_start_time - timespan->get_start();
|
||||
|
||||
for (i = temp.begin(); i != temp.end(); ++i) {
|
||||
|
||||
status.marker = *i;
|
||||
|
||||
if ((*i)->start() < last_end_time) {
|
||||
if ((*i)->is_mark()) {
|
||||
/* Index within track */
|
||||
|
||||
status.index_position = (*i)->start() - timespan->get_start();
|
||||
(this->*index_func) (status);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* A track, defined by a cd range marker or a cd location marker outside of a cd range */
|
||||
|
||||
status.track_position = last_end_time - timespan->get_start();
|
||||
status.track_start_frame = (*i)->start() - timespan->get_start(); // everything before this is the pregap
|
||||
status.track_duration = 0;
|
||||
|
||||
if ((*i)->is_mark()) {
|
||||
// a mark track location needs to look ahead to the next marker's start to determine length
|
||||
nexti = i;
|
||||
++nexti;
|
||||
|
||||
if (nexti != temp.end()) {
|
||||
status.track_duration = (*nexti)->start() - last_end_time;
|
||||
|
||||
last_start_time = (*i)->start();
|
||||
last_end_time = (*nexti)->start();
|
||||
} else {
|
||||
// this was the last marker, use timespan end
|
||||
status.track_duration = timespan->get_end() - last_end_time;
|
||||
|
||||
last_start_time = (*i)->start();
|
||||
last_end_time = timespan->get_end();
|
||||
}
|
||||
} else {
|
||||
// range
|
||||
status.track_duration = (*i)->end() - last_end_time;
|
||||
|
||||
last_start_time = (*i)->start();
|
||||
last_end_time = (*i)->end();
|
||||
}
|
||||
|
||||
(this->*track_func) (status);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportHandler::write_cue_header (CDMarkerStatus & status)
|
||||
{
|
||||
Glib::ustring title = status.timespan->name().compare ("Session") ? status.timespan->name() : (Glib::ustring) session.name();
|
||||
|
||||
status.out << "REM Cue file generated by Ardour" << endl;
|
||||
status.out << "TITLE \"" << title << "\"" << endl;
|
||||
|
||||
// TODO
|
||||
if (!status.format->format_name().compare ("WAV")) {
|
||||
status.out << "FILE " << status.filename << " WAVE" << endl;
|
||||
} else {
|
||||
status.out << "FILE " << status.filename << ' ' << status.format->format_name() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportHandler::write_toc_header (CDMarkerStatus & status)
|
||||
{
|
||||
Glib::ustring title = status.timespan->name().compare ("Session") ? status.timespan->name() : (Glib::ustring) session.name();
|
||||
|
||||
status.out << "CD_DA" << endl;
|
||||
status.out << "CD_TEXT {" << endl << " LANGUAGE_MAP {" << endl << " 0 : EN" << endl << " }" << endl;
|
||||
status.out << " LANGUAGE 0 {" << endl << " TITLE \"" << title << "\"" << endl << " }" << endl << "}" << endl;
|
||||
}
|
||||
|
||||
void
|
||||
ExportHandler::write_track_info_cue (CDMarkerStatus & status)
|
||||
{
|
||||
gchar buf[18];
|
||||
|
||||
status.out << endl << "TRACK " << status.track_number << " AUDIO" << endl;
|
||||
status.out << "FLAGS " ;
|
||||
|
||||
if (status.marker->cd_info.find("scms") != status.marker->cd_info.end()) {
|
||||
status.out << "SCMS ";
|
||||
} else {
|
||||
status.out << "DCP ";
|
||||
}
|
||||
|
||||
if (status.marker->cd_info.find("preemph") != status.marker->cd_info.end()) {
|
||||
status.out << "PRE";
|
||||
}
|
||||
status.out << endl;
|
||||
|
||||
if (status.marker->cd_info.find("isrc") != status.marker->cd_info.end()) {
|
||||
status.out << "ISRC " << status.marker->cd_info["isrc"] << endl;
|
||||
|
||||
}
|
||||
if (status.marker->name() != "") {
|
||||
status.out << "TITLE \"" << status.marker->name() << "\"" << endl;
|
||||
}
|
||||
|
||||
if (status.marker->cd_info.find("performer") != status.marker->cd_info.end()) {
|
||||
status.out << "PERFORMER \"" << status.marker->cd_info["performer"] << "\"" << endl;
|
||||
}
|
||||
|
||||
if (status.marker->cd_info.find("string_composer") != status.marker->cd_info.end()) {
|
||||
status.out << "SONGWRITER \"" << status.marker->cd_info["string_composer"] << "\"" << endl;
|
||||
}
|
||||
|
||||
frames_to_cd_frames_string (buf, status.track_position);
|
||||
status.out << "INDEX 00" << buf << endl;
|
||||
|
||||
frames_to_cd_frames_string (buf, status.track_start_frame);
|
||||
status.out << "INDEX 01" << buf << endl;
|
||||
|
||||
status.index_number = 2;
|
||||
status.track_number++;
|
||||
}
|
||||
|
||||
void
|
||||
ExportHandler::write_track_info_toc (CDMarkerStatus & status)
|
||||
{
|
||||
gchar buf[18];
|
||||
|
||||
status.out << endl << "TRACK AUDIO" << endl;
|
||||
|
||||
if (status.marker->cd_info.find("scms") != status.marker->cd_info.end()) {
|
||||
status.out << "NO ";
|
||||
}
|
||||
status.out << "COPY" << endl;
|
||||
|
||||
if (status.marker->cd_info.find("preemph") != status.marker->cd_info.end()) {
|
||||
status.out << "PRE_EMPHASIS" << endl;
|
||||
} else {
|
||||
status.out << "NO PRE_EMPHASIS" << endl;
|
||||
}
|
||||
|
||||
if (status.marker->cd_info.find("isrc") != status.marker->cd_info.end()) {
|
||||
status.out << "ISRC \"" << status.marker->cd_info["isrc"] << "\"" << endl;
|
||||
}
|
||||
|
||||
status.out << "CD_TEXT {" << endl << " LANGUAGE 0 {" << endl << " TITLE \"" << status.marker->name() << "\"" << endl;
|
||||
if (status.marker->cd_info.find("performer") != status.marker->cd_info.end()) {
|
||||
status.out << " PERFORMER \"" << status.marker->cd_info["performer"] << "\"" << endl;
|
||||
}
|
||||
if (status.marker->cd_info.find("string_composer") != status.marker->cd_info.end()) {
|
||||
status.out << " COMPOSER \"" << status.marker->cd_info["string_composer"] << "\"" << endl;
|
||||
}
|
||||
|
||||
if (status.marker->cd_info.find("isrc") != status.marker->cd_info.end()) {
|
||||
status.out << " ISRC \"";
|
||||
status.out << status.marker->cd_info["isrc"].substr(0,2) << "-";
|
||||
status.out << status.marker->cd_info["isrc"].substr(2,3) << "-";
|
||||
status.out << status.marker->cd_info["isrc"].substr(5,2) << "-";
|
||||
status.out << status.marker->cd_info["isrc"].substr(7,5) << "\"" << endl;
|
||||
}
|
||||
|
||||
status.out << " }" << endl << "}" << endl;
|
||||
|
||||
frames_to_cd_frames_string (buf, status.track_position);
|
||||
status.out << "FILE \"" << status.filename << "\" " << buf;
|
||||
|
||||
frames_to_cd_frames_string (buf, status.track_duration);
|
||||
status.out << buf << endl;
|
||||
|
||||
frames_to_cd_frames_string (buf, status.track_start_frame - status.track_position);
|
||||
status.out << "START" << buf << endl;
|
||||
}
|
||||
|
||||
void
|
||||
ExportHandler::write_index_info_cue (CDMarkerStatus & status)
|
||||
{
|
||||
gchar buf[18];
|
||||
|
||||
snprintf (buf, sizeof(buf), "INDEX %02d", cue_indexnum);
|
||||
status.out << buf;
|
||||
frames_to_cd_frames_string (buf, status.index_position);
|
||||
status.out << buf << endl;
|
||||
|
||||
cue_indexnum++;
|
||||
}
|
||||
|
||||
void
|
||||
ExportHandler::write_index_info_toc (CDMarkerStatus & status)
|
||||
{
|
||||
gchar buf[18];
|
||||
|
||||
frames_to_cd_frames_string (buf, status.index_position - status.track_position);
|
||||
status.out << "INDEX" << buf << endl;
|
||||
}
|
||||
|
||||
void
|
||||
ExportHandler::frames_to_cd_frames_string (char* buf, nframes_t when)
|
||||
{
|
||||
nframes_t remainder;
|
||||
nframes_t fr = session.nominal_frame_rate();
|
||||
int mins, secs, frames;
|
||||
|
||||
mins = when / (60 * fr);
|
||||
remainder = when - (mins * 60 * fr);
|
||||
secs = remainder / fr;
|
||||
remainder -= secs * fr;
|
||||
frames = remainder / (fr / 75);
|
||||
sprintf (buf, " %02d:%02d:%02d", mins, secs, frames);
|
||||
}
|
||||
|
||||
void
|
||||
ExportHandler::start_timespan ()
|
||||
{
|
||||
session.export_status.timespan++;
|
||||
|
||||
if (config_map.empty()) {
|
||||
session.finalize_audio_export();
|
||||
return;
|
||||
}
|
||||
|
||||
current_timespan = config_map.begin()->first;
|
||||
|
||||
/* Register channel configs with timespan */
|
||||
|
||||
timespan_bounds = config_map.equal_range (current_timespan);
|
||||
|
||||
for (ConfigMap::iterator it = timespan_bounds.first; it != timespan_bounds.second; ++it) {
|
||||
it->second.channel_config->register_with_timespan (current_timespan);
|
||||
}
|
||||
|
||||
/* connect stuff and start export */
|
||||
|
||||
current_timespan->process_connection = session.ProcessExport.connect (sigc::mem_fun (*current_timespan, &ExportTimespan::process));
|
||||
session.start_audio_export (current_timespan->get_start(), realtime);
|
||||
}
|
||||
|
||||
void
|
||||
ExportHandler::finish_timespan ()
|
||||
{
|
||||
current_timespan->process_connection.disconnect ();
|
||||
|
||||
/* Register formats and filenames to relevant channel configs */
|
||||
|
||||
session.export_status.total_formats = 0;
|
||||
session.export_status.format = 0;
|
||||
|
||||
for (ConfigMap::iterator it = timespan_bounds.first; it != timespan_bounds.second; ++it) {
|
||||
|
||||
session.export_status.total_formats++;
|
||||
|
||||
/* Setup filename */
|
||||
|
||||
it->second.filename->set_timespan (current_timespan);
|
||||
it->second.filename->set_channel_config (it->second.channel_config);
|
||||
|
||||
/* Do actual registration */
|
||||
|
||||
ChannelConfigPtr chan_config = it->second.channel_config;
|
||||
chan_config->register_file_config (it->second.format, it->second.filename);
|
||||
}
|
||||
|
||||
/* Start writing files by doing a manual call to timespan_thread_finished */
|
||||
|
||||
current_map_it = timespan_bounds.first;
|
||||
timespan_thread_finished ();
|
||||
}
|
||||
|
||||
void
|
||||
ExportHandler::timespan_thread_finished ()
|
||||
{
|
||||
channel_config_connection.disconnect();
|
||||
|
||||
if (current_map_it != timespan_bounds.second) {
|
||||
|
||||
/* Get next configuration as long as no new export process is started */
|
||||
|
||||
ChannelConfigPtr cc = current_map_it->second.channel_config;
|
||||
while (!cc->write_files(processor)) {
|
||||
|
||||
++current_map_it;
|
||||
|
||||
if (current_map_it == timespan_bounds.second) {
|
||||
|
||||
/* reached end of bounds, this call will end up in the else block below */
|
||||
|
||||
timespan_thread_finished ();
|
||||
return;
|
||||
}
|
||||
|
||||
cc = current_map_it->second.channel_config;
|
||||
}
|
||||
|
||||
channel_config_connection = cc->FilesWritten.connect (sigc::mem_fun (*this, &ExportHandler::timespan_thread_finished));
|
||||
++current_map_it;
|
||||
|
||||
} else { /* All files are written from current timespan, reset timespan and start new */
|
||||
|
||||
/* Unregister configs and remove configs with this timespan */
|
||||
|
||||
for (ConfigMap::iterator it = timespan_bounds.first; it != timespan_bounds.second;) {
|
||||
it->second.channel_config->unregister_all ();
|
||||
|
||||
ConfigMap::iterator to_erase = it;
|
||||
++it;
|
||||
config_map.erase (to_erase);
|
||||
}
|
||||
|
||||
/* Start new timespan */
|
||||
|
||||
start_timespan ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ARDOUR
|
||||
677
libs/ardour/export_multiplication.cc
Normal file
677
libs/ardour/export_multiplication.cc
Normal file
|
|
@ -0,0 +1,677 @@
|
|||
/* This file is not used at the moment. It includes code related to export a
|
||||
* multiplication graph system that can be used together with the ExportMultiplicator
|
||||
* class in the gtk2_ardour folder.
|
||||
* - Sakari Bergen 6.8.2008 -
|
||||
*/
|
||||
|
||||
void
|
||||
ExportProfileManager::register_all_configs ()
|
||||
{
|
||||
list<TimespanNodePtr>::iterator tsl_it; // timespan list node iterator
|
||||
for (tsl_it = graph.timespans.begin(); tsl_it != graph.timespans.end(); ++tsl_it) {
|
||||
list<GraphNode *>::const_iterator cc_it; // channel config node iterator
|
||||
for (cc_it = (*tsl_it)->get_children().begin(); cc_it != (*tsl_it)->get_children().end(); ++cc_it) {
|
||||
list<GraphNode *>::const_iterator f_it; // format node iterator
|
||||
for (f_it = (*cc_it)->get_children().begin(); f_it != (*cc_it)->get_children().end(); ++f_it) {
|
||||
list<GraphNode *>::const_iterator fn_it; // filename node iterator
|
||||
for (fn_it = (*f_it)->get_children().begin(); fn_it != (*f_it)->get_children().end(); ++fn_it) {
|
||||
/* Finally loop through each timespan in the timespan list */
|
||||
|
||||
TimespanNodePtr ts_node;
|
||||
if (!(ts_node = boost::dynamic_pointer_cast<TimespanNode> (*tsl_it))) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "Invalid pointer cast in ExportProfileManager");
|
||||
}
|
||||
|
||||
TimespanListPtr ts_list = ts_node->data()->timespans;
|
||||
TimespanList::iterator ts_it;
|
||||
for (ts_it = ts_list->begin(); ts_it != ts_list->end(); ++ts_it) {
|
||||
|
||||
TimespanPtr timespan = *ts_it;
|
||||
|
||||
ChannelConfigNode * cc_node;
|
||||
if (!(cc_node = dynamic_cast<ChannelConfigNode *> (*cc_it))) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "Invalid pointer cast in ExportProfileManager");
|
||||
}
|
||||
ChannelConfigPtr channel_config = cc_node->data()->config;
|
||||
|
||||
FormatNode * f_node;
|
||||
if (!(f_node = dynamic_cast<FormatNode *> (*f_it))) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "Invalid pointer cast in ExportProfileManager");
|
||||
}
|
||||
FormatPtr format = f_node->data()->format;
|
||||
|
||||
FilenameNode * fn_node;
|
||||
if (!(fn_node = dynamic_cast<FilenameNode *> (*fn_it))) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "Invalid pointer cast in ExportProfileManager");
|
||||
}
|
||||
FilenamePtr filename = fn_node->data()->filename;
|
||||
|
||||
handler->add_export_config (timespan, channel_config, format, filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::create_empty_config ()
|
||||
{
|
||||
TimespanNodePtr timespan = TimespanNode::create (new TimespanState ());
|
||||
timespan->data()->timespans->push_back (handler->add_timespan());
|
||||
|
||||
ChannelConfigNodePtr channel_config = ChannelConfigNode::create (new ChannelConfigState(handler->add_channel_config()));
|
||||
|
||||
FormatNodePtr format;
|
||||
load_formats ();
|
||||
if (!format_list.empty()) {
|
||||
format = FormatNode::create (new FormatState (*format_list.begin ()));
|
||||
} else {
|
||||
format = FormatNode::create (new FormatState (handler->add_format ()));
|
||||
}
|
||||
|
||||
FilenameNodePtr filename = FilenameNode::create (new FilenameState (handler->add_filename()));
|
||||
|
||||
/* Bring everything together */
|
||||
|
||||
timespan->add_child (channel_config.get(), 0);
|
||||
channel_config->add_child (format.get(), 0);
|
||||
format->add_child (filename.get(), 0);
|
||||
|
||||
graph.timespans.push_back (timespan);
|
||||
graph.channel_configs.push_back (channel_config);
|
||||
graph.formats.push_back (format);
|
||||
graph.filenames.push_back (filename);
|
||||
}
|
||||
|
||||
/*** GraphNode ***/
|
||||
|
||||
uint32_t ExportProfileManager::GraphNode::id_counter = 0;
|
||||
|
||||
ExportProfileManager::GraphNode::GraphNode ()
|
||||
{
|
||||
_id = ++id_counter;
|
||||
}
|
||||
|
||||
ExportProfileManager::GraphNode::~GraphNode ()
|
||||
{
|
||||
while (!children.empty()) {
|
||||
remove_child (children.front());
|
||||
}
|
||||
|
||||
while (!parents.empty()) {
|
||||
parents.front()->remove_child (this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::GraphNode::add_parent (GraphNode * parent)
|
||||
{
|
||||
for (list<GraphNode *>::iterator it = parents.begin(); it != parents.end(); ++it) {
|
||||
if (*it == parent) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
parents.push_back (parent);
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::GraphNode::add_child (GraphNode * child, GraphNode * left_sibling)
|
||||
{
|
||||
for (list<GraphNode *>::iterator it = children.begin(); it != children.end(); ++it) {
|
||||
if (*it == child) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (left_sibling) {
|
||||
insert_after (children, left_sibling, child);
|
||||
} else {
|
||||
children.push_back (child);
|
||||
}
|
||||
|
||||
child->add_parent (this);
|
||||
}
|
||||
|
||||
bool
|
||||
ExportProfileManager::GraphNode::is_ancestor_of (GraphNode const * node) const
|
||||
{
|
||||
for (list<GraphNode *>::const_iterator it = children.begin(); it != children.end(); ++it) {
|
||||
if (*it == node || (*it)->is_ancestor_of (node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ExportProfileManager::GraphNode::is_descendant_of (GraphNode const * node) const
|
||||
{
|
||||
for (list<GraphNode *>::const_iterator it = parents.begin(); it != parents.end(); ++it) {
|
||||
if (*it == node || (*it)->is_descendant_of (node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::GraphNode::select (bool value)
|
||||
{
|
||||
if (_selected == value) { return; }
|
||||
|
||||
_selected = value;
|
||||
SelectChanged (value);
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::GraphNode::remove_parent (GraphNode * parent)
|
||||
{
|
||||
for (list<GraphNode *>::iterator it = parents.begin(); it != parents.end(); ++it) {
|
||||
if (*it == parent) {
|
||||
parents.erase (it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::GraphNode::remove_child (GraphNode * child)
|
||||
{
|
||||
for (list<GraphNode *>::iterator it = children.begin(); it != children.end(); ++it) {
|
||||
if (*it == child) {
|
||||
children.erase (it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
child->remove_parent (this);
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::split_node (GraphNode * node, float position)
|
||||
{
|
||||
TimespanNode * ts_node;
|
||||
if ((ts_node = dynamic_cast<TimespanNode *> (node))) {
|
||||
split_timespan (ts_node->self_ptr(), position);
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelConfigNode * cc_node;
|
||||
if ((cc_node = dynamic_cast<ChannelConfigNode *> (node))) {
|
||||
split_channel_config (cc_node->self_ptr(), position);
|
||||
return;
|
||||
}
|
||||
|
||||
FormatNode * f_node;
|
||||
if ((f_node = dynamic_cast<FormatNode *> (node))) {
|
||||
split_format (f_node->self_ptr(), position);
|
||||
return;
|
||||
}
|
||||
|
||||
FilenameNode * fn_node;
|
||||
if ((fn_node = dynamic_cast<FilenameNode *> (node))) {
|
||||
split_filename (fn_node->self_ptr(), position);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::remove_node (GraphNode * node)
|
||||
{
|
||||
TimespanNode * ts_node;
|
||||
if ((ts_node = dynamic_cast<TimespanNode *> (node))) {
|
||||
remove_timespan (ts_node->self_ptr());
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelConfigNode * cc_node;
|
||||
if ((cc_node = dynamic_cast<ChannelConfigNode *> (node))) {
|
||||
remove_channel_config (cc_node->self_ptr());
|
||||
return;
|
||||
}
|
||||
|
||||
FormatNode * f_node;
|
||||
if ((f_node = dynamic_cast<FormatNode *> (node))) {
|
||||
remove_format (f_node->self_ptr());
|
||||
return;
|
||||
}
|
||||
|
||||
FilenameNode * fn_node;
|
||||
if ((fn_node = dynamic_cast<FilenameNode *> (node))) {
|
||||
remove_filename (fn_node->self_ptr());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::purge_graph ()
|
||||
{
|
||||
for (list<TimespanNodePtr>::iterator it = graph.timespans.begin(); it != graph.timespans.end(); ) {
|
||||
list<TimespanNodePtr>::iterator tmp = it;
|
||||
++it;
|
||||
|
||||
if ((*tmp)->get_children().empty()) {
|
||||
graph.timespans.erase (tmp);
|
||||
}
|
||||
}
|
||||
|
||||
for (list<ChannelConfigNodePtr>::iterator it = graph.channel_configs.begin(); it != graph.channel_configs.end(); ) {
|
||||
list<ChannelConfigNodePtr>::iterator tmp = it;
|
||||
++it;
|
||||
|
||||
if ((*tmp)->get_parents().empty()) {
|
||||
graph.channel_configs.erase (tmp);
|
||||
}
|
||||
}
|
||||
|
||||
for (list<FormatNodePtr>::iterator it = graph.formats.begin(); it != graph.formats.end(); ) {
|
||||
list<FormatNodePtr>::iterator tmp = it;
|
||||
++it;
|
||||
|
||||
if ((*tmp)->get_parents().empty()) {
|
||||
graph.formats.erase (tmp);
|
||||
}
|
||||
}
|
||||
|
||||
for (list<FilenameNodePtr>::iterator it = graph.filenames.begin(); it != graph.filenames.end(); ) {
|
||||
list<FilenameNodePtr>::iterator tmp = it;
|
||||
++it;
|
||||
|
||||
if ((*tmp)->get_parents().empty()) {
|
||||
graph.filenames.erase (tmp);
|
||||
}
|
||||
}
|
||||
|
||||
GraphChanged();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
ExportProfileManager::insert_after (list<T> & the_list, T const & position, T const & element)
|
||||
{
|
||||
typename list<T>::iterator it;
|
||||
for (it = the_list.begin(); it != the_list.end(); ++it) {
|
||||
if (*it == position) {
|
||||
the_list.insert (++it, element);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "invalid position given to ExportProfileManager::insert_after (aborting)" << std::endl;
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
ExportProfileManager::remove_by_element (list<T> & the_list, T const & element)
|
||||
{
|
||||
typename list<T>::iterator it;
|
||||
for (it = the_list.begin(); it != the_list.end(); ++it) {
|
||||
if (*it == element) {
|
||||
the_list.erase (it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ExportProfileManager::nodes_have_one_common_child (list<GraphNode *> const & the_list)
|
||||
{
|
||||
return end_of_common_child_range (the_list, the_list.begin()) == --the_list.end();
|
||||
}
|
||||
|
||||
list<ExportProfileManager::GraphNode *>::const_iterator
|
||||
ExportProfileManager::end_of_common_child_range (list<GraphNode *> const & the_list, list<GraphNode *>::const_iterator beginning)
|
||||
{
|
||||
if ((*beginning)->get_children().size() != 1) { return beginning; }
|
||||
GraphNode * child = (*beginning)->get_children().front();
|
||||
|
||||
list<GraphNode *>::const_iterator it = beginning;
|
||||
while (it != the_list.end() && (*it)->get_children().size() == 1 && (*it)->get_children().front() == child) {
|
||||
++it;
|
||||
}
|
||||
|
||||
return --it;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::split_node_at_position (GraphNode * old_node, GraphNode * new_node, float position)
|
||||
{
|
||||
list<GraphNode *> const & node_parents = old_node->get_parents();
|
||||
uint32_t split_index = (int) (node_parents.size() * position + 0.5);
|
||||
split_index = std::max ((uint32_t) 1, std::min (split_index, node_parents.size() - 1));
|
||||
|
||||
list<GraphNode *>::const_iterator it = node_parents.begin();
|
||||
for (uint32_t index = 1; it != node_parents.end(); ++index) {
|
||||
if (index > split_index) {
|
||||
list<GraphNode *>::const_iterator tmp = it++;
|
||||
(*tmp)->add_child (new_node, old_node);
|
||||
(*tmp)->remove_child (old_node);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::split_timespan (TimespanNodePtr node, float)
|
||||
{
|
||||
TimespanNodePtr new_timespan = duplicate_timespan_node (node);
|
||||
insert_after (graph.timespans, node, new_timespan);
|
||||
|
||||
/* Note: Since a timespan selector allows all combinations of ranges
|
||||
* there is no reason for a channel configuration to have two parents
|
||||
*/
|
||||
|
||||
duplicate_timespan_children (node->self_ptr(), new_timespan);
|
||||
|
||||
GraphChanged();
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::split_channel_config (ChannelConfigNodePtr node, float)
|
||||
{
|
||||
ChannelConfigNodePtr new_config = duplicate_channel_config_node (node);
|
||||
insert_after (graph.channel_configs, node, new_config);
|
||||
|
||||
/* Channel configs have only one parent, see above! */
|
||||
node->get_parents().front()->add_child (new_config.get(), node.get());
|
||||
|
||||
if (node->get_children().size() == 1) {
|
||||
new_config->add_child (node->first_child(), 0);
|
||||
} else {
|
||||
duplicate_channel_config_children (node, new_config);
|
||||
}
|
||||
|
||||
GraphChanged();
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::split_format (FormatNodePtr node, float position)
|
||||
{
|
||||
FormatNodePtr new_format = duplicate_format_node (node);
|
||||
insert_after (graph.formats, node, new_format);
|
||||
|
||||
list<GraphNode *> const & node_parents = node->get_parents();
|
||||
if (node_parents.size() == 1) {
|
||||
node_parents.front()->add_child (new_format.get(), 0);
|
||||
} else {
|
||||
node->sort_parents (graph.channel_configs);
|
||||
split_node_at_position (node.get(), new_format.get(), position);
|
||||
}
|
||||
|
||||
if (node->get_children().size() == 1) {
|
||||
new_format->add_child (node->first_child(), 0);
|
||||
} else {
|
||||
duplicate_format_children (node, new_format);
|
||||
}
|
||||
|
||||
GraphChanged();
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::split_filename (FilenameNodePtr node, float position)
|
||||
{
|
||||
FilenameNodePtr new_filename = duplicate_filename_node (node);
|
||||
insert_after (graph.filenames, node, new_filename);
|
||||
|
||||
list<GraphNode *> const & node_parents = node->get_parents();
|
||||
|
||||
if (node_parents.size() == 1) {
|
||||
node_parents.front()->add_child (new_filename.get(), 0);
|
||||
} else {
|
||||
node->sort_parents (graph.formats);
|
||||
split_node_at_position (node.get(), new_filename.get(), position);
|
||||
}
|
||||
|
||||
GraphChanged();
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::duplicate_timespan_children (TimespanNodePtr source, TimespanNodePtr target, GraphNode * insertion_point)
|
||||
{
|
||||
list<GraphNode *> const & source_children = source->get_children();
|
||||
bool one_grandchild = nodes_have_one_common_child (source_children);
|
||||
GraphNode * child_insertion_point = 0;
|
||||
|
||||
ChannelConfigNodePtr node_insertion_point;
|
||||
ChannelConfigNode * node_insertion_ptr;
|
||||
if (!insertion_point) { insertion_point = source->last_child(); }
|
||||
if (!(node_insertion_ptr = dynamic_cast<ChannelConfigNode *> (insertion_point))) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "Invalid pointer cast in ExportProfileManager");
|
||||
}
|
||||
node_insertion_point = node_insertion_ptr->self_ptr();
|
||||
|
||||
/* Keep track of common children */
|
||||
|
||||
list<GraphNode *>::const_iterator common_children_begin = source_children.begin();
|
||||
list<GraphNode *>::const_iterator common_children_end = end_of_common_child_range (source_children, source_children.begin());
|
||||
GraphNode * common_child = 0;
|
||||
|
||||
for (list<GraphNode *>::const_iterator it = source_children.begin(); it != source_children.end(); ++it) {
|
||||
/* Duplicate node */
|
||||
|
||||
ChannelConfigNode * node;
|
||||
ChannelConfigNodePtr new_node;
|
||||
|
||||
if (!(node = dynamic_cast<ChannelConfigNode *> (*it))) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "Invalid pointer cast in ExportProfileManager");
|
||||
}
|
||||
|
||||
new_node = duplicate_channel_config_node (node->self_ptr());
|
||||
|
||||
/* Insert in gaph's list and update insertion position */
|
||||
|
||||
insert_after (graph.channel_configs, node_insertion_point, new_node);
|
||||
node_insertion_point = new_node;
|
||||
|
||||
/* Handle children */
|
||||
|
||||
target->add_child (new_node.get(), child_insertion_point);
|
||||
child_insertion_point = new_node.get();
|
||||
|
||||
if (one_grandchild) {
|
||||
new_node->add_child (node->first_child(), 0);
|
||||
} else {
|
||||
list<GraphNode *>::const_iterator past_end = common_children_end;
|
||||
if (it == ++past_end) { // At end => start new range
|
||||
common_children_begin = it;
|
||||
common_children_end = end_of_common_child_range (source_children, it);
|
||||
}
|
||||
|
||||
if (it == common_children_begin) { // At beginning => do duplication
|
||||
GraphNode * grand_child_ins_pt = common_child;
|
||||
if (!grand_child_ins_pt) {
|
||||
grand_child_ins_pt = source->last_child()->last_child();
|
||||
}
|
||||
duplicate_channel_config_children (node->self_ptr(), new_node, grand_child_ins_pt);
|
||||
common_child = new_node->first_child();
|
||||
} else { // Somewhere in between end and beginning => share child
|
||||
new_node->add_child (common_child, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::duplicate_channel_config_children (ChannelConfigNodePtr source, ChannelConfigNodePtr target, GraphNode * insertion_point)
|
||||
{
|
||||
list<GraphNode *> const & source_children = source->get_children();
|
||||
bool one_grandchild = nodes_have_one_common_child (source_children);
|
||||
GraphNode * child_insertion_point = 0;
|
||||
|
||||
FormatNodePtr node_insertion_point;
|
||||
FormatNode * node_insertion_ptr;
|
||||
if (!insertion_point) { insertion_point = source->last_child(); }
|
||||
if (!(node_insertion_ptr = dynamic_cast<FormatNode *> (insertion_point))) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "Invalid pointer cast in ExportProfileManager");
|
||||
}
|
||||
node_insertion_point = node_insertion_ptr->self_ptr();
|
||||
|
||||
/* Keep track of common children */
|
||||
|
||||
list<GraphNode *>::const_iterator common_children_begin = source_children.begin();
|
||||
list<GraphNode *>::const_iterator common_children_end = end_of_common_child_range (source_children, source_children.begin());
|
||||
GraphNode * common_child = 0;
|
||||
|
||||
for (list<GraphNode *>::const_iterator it = source_children.begin(); it != source_children.end(); ++it) {
|
||||
/* Duplicate node */
|
||||
|
||||
FormatNode * node;
|
||||
FormatNodePtr new_node;
|
||||
|
||||
if (!(node = dynamic_cast<FormatNode *> (*it))) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "Invalid pointer cast in ExportProfileManager");
|
||||
}
|
||||
|
||||
new_node = duplicate_format_node (node->self_ptr());
|
||||
|
||||
/* Insert in gaph's list and update insertion position */
|
||||
|
||||
insert_after (graph.formats, node_insertion_point, new_node);
|
||||
node_insertion_point = new_node;
|
||||
|
||||
/* Handle children */
|
||||
|
||||
target->add_child (new_node.get(), child_insertion_point);
|
||||
child_insertion_point = new_node.get();
|
||||
|
||||
if (one_grandchild) {
|
||||
new_node->add_child (node->first_child(), 0);
|
||||
} else {
|
||||
list<GraphNode *>::const_iterator past_end = common_children_end;
|
||||
if (it == ++past_end) { // At end => start new range
|
||||
common_children_begin = it;
|
||||
common_children_end = end_of_common_child_range (source_children, it);
|
||||
}
|
||||
|
||||
if (it == common_children_begin) { // At beginning => do duplication
|
||||
GraphNode * grand_child_ins_pt = common_child;
|
||||
if (!grand_child_ins_pt) {
|
||||
grand_child_ins_pt = source->get_parents().back()->last_child()->last_child()->last_child();
|
||||
}
|
||||
duplicate_format_children (node->self_ptr(), new_node, grand_child_ins_pt);
|
||||
common_child = new_node->first_child();
|
||||
} else { // Somewhere in between end and beginning => share child
|
||||
new_node->add_child (common_child, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::duplicate_format_children (FormatNodePtr source, FormatNodePtr target, GraphNode * insertion_point)
|
||||
{
|
||||
GraphNode * child_insertion_point = 0;
|
||||
|
||||
FilenameNodePtr node_insertion_point;
|
||||
FilenameNode * node_insertion_ptr;
|
||||
if (!insertion_point) { insertion_point = source->last_child(); }
|
||||
if (!(node_insertion_ptr = dynamic_cast<FilenameNode *> (insertion_point))) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "Invalid pointer cast in ExportProfileManager");
|
||||
}
|
||||
node_insertion_point = node_insertion_ptr->self_ptr();
|
||||
|
||||
for (list<GraphNode *>::const_iterator it = source->get_children().begin(); it != source->get_children().end(); ++it) {
|
||||
/* Duplicate node */
|
||||
|
||||
FilenameNode * node;
|
||||
FilenameNodePtr new_node;
|
||||
|
||||
if (!(node = dynamic_cast<FilenameNode *> (*it))) {
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "Invalid pointer cast in ExportProfileManager");
|
||||
}
|
||||
|
||||
new_node = duplicate_filename_node (node->self_ptr());
|
||||
|
||||
/* Insert in gaph's list and update insertion position */
|
||||
|
||||
insert_after (graph.filenames, node_insertion_point, new_node);
|
||||
node_insertion_point = new_node;
|
||||
|
||||
/* Handle children */
|
||||
|
||||
target->add_child (new_node.get(), child_insertion_point);
|
||||
child_insertion_point = new_node.get();
|
||||
}
|
||||
}
|
||||
|
||||
ExportProfileManager::TimespanNodePtr
|
||||
ExportProfileManager::duplicate_timespan_node (TimespanNodePtr node)
|
||||
{
|
||||
TimespanStatePtr state = node->data();
|
||||
TimespanStatePtr new_state (new TimespanState ());
|
||||
TimespanNodePtr new_node = TimespanNode::create (new_state);
|
||||
|
||||
for (TimespanList::iterator it = state->timespans->begin(); it != state->timespans->end(); ++it) {
|
||||
new_state->timespans->push_back (handler->add_timespan_copy (*it));
|
||||
}
|
||||
|
||||
new_state->time_format = state->time_format;
|
||||
new_state->marker_format = state->marker_format;
|
||||
|
||||
return new_node;
|
||||
}
|
||||
|
||||
ExportProfileManager::ChannelConfigNodePtr
|
||||
ExportProfileManager::duplicate_channel_config_node (ChannelConfigNodePtr node)
|
||||
{
|
||||
ChannelConfigStatePtr state = node->data();
|
||||
ChannelConfigStatePtr new_state (new ChannelConfigState (handler->add_channel_config_copy (state->config)));
|
||||
ChannelConfigNodePtr new_node = ChannelConfigNode::create (new_state);
|
||||
|
||||
return new_node;
|
||||
}
|
||||
|
||||
ExportProfileManager::FormatNodePtr
|
||||
ExportProfileManager::duplicate_format_node (FormatNodePtr node)
|
||||
{
|
||||
FormatStatePtr state = node->data();
|
||||
FormatStatePtr new_state (new FormatState (handler->add_format_copy (state->format)));
|
||||
FormatNodePtr new_node = FormatNode::create (new_state);
|
||||
|
||||
return new_node;
|
||||
}
|
||||
|
||||
ExportProfileManager::FilenameNodePtr
|
||||
ExportProfileManager::duplicate_filename_node (FilenameNodePtr node)
|
||||
{
|
||||
FilenameStatePtr state = node->data();
|
||||
FilenameStatePtr new_state (new FilenameState (handler->add_filename_copy (state->filename)));
|
||||
FilenameNodePtr new_node = FilenameNode::create (new_state);
|
||||
|
||||
return new_node;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::remove_timespan (TimespanNodePtr node)
|
||||
{
|
||||
remove_by_element (graph.timespans, node);
|
||||
purge_graph ();
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::remove_channel_config (ChannelConfigNodePtr node)
|
||||
{
|
||||
remove_by_element (graph.channel_configs, node);
|
||||
purge_graph ();
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::remove_format (FormatNodePtr node)
|
||||
{
|
||||
remove_by_element (graph.formats, node);
|
||||
purge_graph ();
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::remove_filename (FilenameNodePtr node)
|
||||
{
|
||||
remove_by_element (graph.filenames, node);
|
||||
purge_graph ();
|
||||
}
|
||||
347
libs/ardour/export_processor.cc
Normal file
347
libs/ardour/export_processor.cc
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/export_processor.h>
|
||||
|
||||
#include <pbd/error.h>
|
||||
#include <pbd/filesystem.h>
|
||||
|
||||
#include <ardour/session.h>
|
||||
#include <ardour/audiofile_tagger.h>
|
||||
#include <ardour/broadcast_info.h>
|
||||
#include <ardour/export_failed.h>
|
||||
#include <ardour/export_filename.h>
|
||||
#include <ardour/export_status.h>
|
||||
#include <ardour/export_format_specification.h>
|
||||
#include <ardour/sndfile_helpers.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace PBD;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
sigc::signal<void, Glib::ustring> ExportProcessor::WritingFile;
|
||||
|
||||
ExportProcessor::ExportProcessor (Session & session) :
|
||||
session (session),
|
||||
status (session.export_status),
|
||||
blocksize (session.get_block_size()),
|
||||
frame_rate (session.frame_rate())
|
||||
{
|
||||
reset ();
|
||||
}
|
||||
|
||||
ExportProcessor::~ExportProcessor ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ExportProcessor::reset ()
|
||||
{
|
||||
file_sinks.clear();
|
||||
writer_list.clear();
|
||||
filename.reset();
|
||||
normalizer.reset();
|
||||
src.reset();
|
||||
peak_reader.reset();
|
||||
temp_file.reset();
|
||||
}
|
||||
|
||||
int
|
||||
ExportProcessor::prepare (FormatPtr format, FilenamePtr fname, uint32_t chans, bool split, nframes_t start)
|
||||
{
|
||||
session.export_status.format++;
|
||||
temp_file_length = 0;
|
||||
|
||||
/* Reset just to be sure all references are dropped */
|
||||
|
||||
reset();
|
||||
|
||||
/* Get parameters needed later on */
|
||||
|
||||
channels = chans;
|
||||
split_files = split;
|
||||
filename = fname;
|
||||
tag = format->tag();
|
||||
broadcast_info = format->has_broadcast_info();
|
||||
normalize = format->normalize();
|
||||
trim_beginning = format->trim_beginning();
|
||||
trim_end = format->trim_end();
|
||||
silence_beginning = format->silence_beginning();
|
||||
silence_end = format->silence_end();
|
||||
|
||||
/* SRC */
|
||||
|
||||
src.reset (new SampleRateConverter (channels, frame_rate, format->sample_rate(), format->src_quality()));
|
||||
|
||||
/* Construct export pipe to temp file */
|
||||
|
||||
status.stage = export_PostProcess;
|
||||
|
||||
if (normalize) {
|
||||
/* Normalizing => we need a normalizer, peak reader and tempfile */
|
||||
|
||||
normalizer.reset (new Normalizer (channels, format->normalize_target()));
|
||||
|
||||
peak_reader.reset (new PeakReader (channels));
|
||||
temp_file.reset (new ExportTempFile (channels, format->sample_rate()));
|
||||
|
||||
src->pipe_to (peak_reader);
|
||||
peak_reader->pipe_to (temp_file);
|
||||
|
||||
} else if (trim_beginning || trim_end) {
|
||||
/* Not normalizing, but silence will be trimmed => need for a tempfile */
|
||||
|
||||
temp_file.reset (new ExportTempFile (channels, format->sample_rate()));
|
||||
src->pipe_to (temp_file);
|
||||
|
||||
} else {
|
||||
/* Due to complexity and time running out, a tempfile will be created for this also... */
|
||||
|
||||
temp_file.reset (new ExportTempFile (channels, format->sample_rate()));
|
||||
src->pipe_to (temp_file);
|
||||
}
|
||||
|
||||
/* File writer(s) */
|
||||
|
||||
FloatSinkPtr (ExportProcessor::*prep_function) (FormatPtr, uint32_t, ustring const &);
|
||||
|
||||
switch (format->type()) {
|
||||
case ExportFormatBase::T_Sndfile:
|
||||
prep_function = &ExportProcessor::prepare_sndfile_writer;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ExportFailed (_("Export failed due to a programming error"), "Invalid format given for ExportProcessor::prepare!");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
/* Ensure directory exists */
|
||||
|
||||
sys::path folder (filename->get_folder());
|
||||
if (!sys::exists (folder)) {
|
||||
if (!sys::create_directory (folder)) {
|
||||
throw ExportFailed ("Export could not create the directory you requested for", "sys::create_directory failed for export dir");
|
||||
}
|
||||
}
|
||||
|
||||
/* prep file sinks */
|
||||
|
||||
if (split) {
|
||||
filename->include_channel = true;
|
||||
for (uint32_t chn = 1; chn <= channels; ++chn) {
|
||||
filename->set_channel (chn);
|
||||
file_sinks.push_back ((this->*prep_function) (format, 1, filename->get_path (format)));
|
||||
WritingFile (filename->get_path (format));
|
||||
}
|
||||
|
||||
} else {
|
||||
file_sinks.push_back ((this->*prep_function) (format, channels, filename->get_path (format)));
|
||||
WritingFile (filename->get_path (format));
|
||||
}
|
||||
|
||||
/* Set position info */
|
||||
|
||||
nframes_t start_position = ((double) format->sample_rate() / frame_rate) * start + 0.5;
|
||||
|
||||
for (FileWriterList::iterator it = writer_list.begin(); it != writer_list.end(); ++it) {
|
||||
(*it)->set_position (start_position);
|
||||
}
|
||||
|
||||
/* set broadcast info if necessary */
|
||||
|
||||
if (broadcast_info) {
|
||||
for (FileWriterList::iterator it = writer_list.begin(); it != writer_list.end(); ++it) {
|
||||
|
||||
BroadcastInfo bci;
|
||||
bci.set_from_session (session, (*it)->position());
|
||||
|
||||
boost::shared_ptr<SndfileWriterBase> sndfile_ptr;
|
||||
if ((sndfile_ptr = boost::dynamic_pointer_cast<SndfileWriterBase> (*it))) {
|
||||
if (!bci.write_to_file (sndfile_ptr->get_sndfile())) {
|
||||
std::cerr << bci.get_error() << std::endl;
|
||||
}
|
||||
} else {
|
||||
if (!bci.write_to_file ((*it)->filename())) {
|
||||
std::cerr << bci.get_error() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
nframes_t
|
||||
ExportProcessor::process (float * data, nframes_t frames)
|
||||
{
|
||||
nframes_t frames_written = src->write (data, frames);
|
||||
temp_file_length += frames_written;
|
||||
return frames_written;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProcessor::prepare_post_processors ()
|
||||
{
|
||||
/* Set end of input and do last write */
|
||||
float dummy;
|
||||
src->set_end_of_input ();
|
||||
src->write (&dummy, 0);
|
||||
|
||||
/* Trim and add silence */
|
||||
|
||||
temp_file->trim_beginning (trim_beginning);
|
||||
temp_file->trim_end (trim_end);
|
||||
|
||||
temp_file->set_silence_beginning (silence_beginning);
|
||||
temp_file->set_silence_end (silence_end);
|
||||
|
||||
/* Set up normalizer */
|
||||
|
||||
if (normalize) {
|
||||
normalizer->set_peak (peak_reader->get_peak ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProcessor::write_files ()
|
||||
{
|
||||
/* Write to disk */
|
||||
|
||||
status.stage = export_Write;
|
||||
temp_file_position = 0;
|
||||
|
||||
uint32_t buffer_size = 4096; // TODO adjust buffer size?
|
||||
float * buf = new float[channels * buffer_size];
|
||||
int frames_read;
|
||||
|
||||
FloatSinkPtr disk_sink;
|
||||
|
||||
if (normalize) {
|
||||
disk_sink = boost::dynamic_pointer_cast<FloatSink> (normalizer);
|
||||
normalizer->pipe_to (file_sinks[0]);
|
||||
} else {
|
||||
disk_sink = file_sinks[0];
|
||||
}
|
||||
|
||||
if (split_files) {
|
||||
|
||||
/* Get buffers for each channel separately */
|
||||
|
||||
std::vector<float *> chan_bufs;
|
||||
|
||||
for (uint32_t i = 0; i < channels; ++i) {
|
||||
chan_bufs.push_back(new float[buffer_size]);
|
||||
}
|
||||
|
||||
/* de-interleave and write files */
|
||||
|
||||
while ((frames_read = temp_file->read (buf, buffer_size)) > 0) {
|
||||
for (uint32_t channel = 0; channel < channels; ++channel) {
|
||||
for (uint32_t i = 0; i < buffer_size; ++i) {
|
||||
chan_bufs[channel][i] = buf[channel + (channels * i)];
|
||||
}
|
||||
if (normalize) {
|
||||
normalizer->pipe_to (file_sinks[channel]);
|
||||
} else {
|
||||
disk_sink = file_sinks[channel];
|
||||
}
|
||||
disk_sink->write (chan_bufs[channel], frames_read);
|
||||
}
|
||||
|
||||
if (status.aborted()) { break; }
|
||||
temp_file_position += frames_read;
|
||||
status.progress = (float) temp_file_position / temp_file_length;
|
||||
}
|
||||
|
||||
/* Clean up */
|
||||
|
||||
for (std::vector<float *>::iterator it = chan_bufs.begin(); it != chan_bufs.end(); ++it) {
|
||||
delete[] *it;
|
||||
}
|
||||
|
||||
} else {
|
||||
while ((frames_read = temp_file->read (buf, buffer_size)) > 0) {
|
||||
disk_sink->write (buf, frames_read);
|
||||
|
||||
if (status.aborted()) { break; }
|
||||
temp_file_position += frames_read;
|
||||
status.progress = (float) temp_file_position / temp_file_length;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] buf;
|
||||
|
||||
/* Tag files if necessary and send exported signal */
|
||||
|
||||
|
||||
for (FileWriterList::iterator it = writer_list.begin(); it != writer_list.end(); ++it) {
|
||||
if (tag) {
|
||||
AudiofileTagger::tag_file ((*it)->filename(), session.metadata());
|
||||
}
|
||||
session.Exported ((*it)->filename(), session.name());
|
||||
}
|
||||
}
|
||||
|
||||
ExportProcessor::FloatSinkPtr
|
||||
ExportProcessor::prepare_sndfile_writer (FormatPtr format, uint32_t channels, ustring const & filename)
|
||||
{
|
||||
int real_format = format->format_id() | format->sample_format() | format->endianness();
|
||||
|
||||
uint32_t data_width = sndfile_data_width (real_format);
|
||||
|
||||
if (data_width == 8 || data_width == 16) {
|
||||
|
||||
ShortConverterPtr sfc = ShortConverterPtr (new SampleFormatConverter<short> (channels, format->dither_type(), data_width));
|
||||
ShortWriterPtr sfw = ShortWriterPtr (new SndfileWriter<short> (channels, format->sample_rate(), real_format, filename));
|
||||
|
||||
writer_list.push_back (boost::static_pointer_cast<ExportFileWriter> (sfw));
|
||||
|
||||
sfc->pipe_to (sfw);
|
||||
return boost::static_pointer_cast<FloatSink> (sfc);
|
||||
|
||||
} else if (data_width == 24 || data_width == 32) {
|
||||
|
||||
IntConverterPtr sfc = IntConverterPtr (new SampleFormatConverter<int> (channels, format->dither_type(), data_width));
|
||||
IntWriterPtr sfw = IntWriterPtr (new SndfileWriter<int> (channels, format->sample_rate(), real_format, filename));
|
||||
|
||||
writer_list.push_back (boost::static_pointer_cast<ExportFileWriter> (sfw));
|
||||
|
||||
sfc->pipe_to (sfw);
|
||||
return boost::static_pointer_cast<FloatSink> (sfc);
|
||||
|
||||
} else {
|
||||
|
||||
FloatConverterPtr sfc = FloatConverterPtr (new SampleFormatConverter<float> (channels, format->dither_type(), data_width));
|
||||
FloatWriterPtr sfw = FloatWriterPtr (new SndfileWriter<float> (channels, format->sample_rate(), real_format, filename));
|
||||
|
||||
writer_list.push_back (boost::static_pointer_cast<ExportFileWriter> (sfw));
|
||||
|
||||
sfc->pipe_to (sfw);
|
||||
return boost::static_pointer_cast<FloatSink> (sfc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}; // namespace ARDOUR
|
||||
927
libs/ardour/export_profile_manager.cc
Normal file
927
libs/ardour/export_profile_manager.cc
Normal file
|
|
@ -0,0 +1,927 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/export_profile_manager.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <glibmm/fileutils.h>
|
||||
|
||||
#include <pbd/enumwriter.h>
|
||||
#include <pbd/xml++.h>
|
||||
#include <pbd/convert.h>
|
||||
|
||||
#include <ardour/audioengine.h>
|
||||
#include <ardour/export_failed.h>
|
||||
#include <ardour/export_format_specification.h>
|
||||
#include <ardour/export_timespan.h>
|
||||
#include <ardour/export_channel_configuration.h>
|
||||
#include <ardour/export_filename.h>
|
||||
#include <ardour/export_handler.h>
|
||||
#include <ardour/session.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace PBD;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
ExportProfileManager::Preset::Preset (string filename, Session & s) :
|
||||
_id (0), session (s), global (filename), local (0)
|
||||
{
|
||||
XMLNode * root;
|
||||
if ((root = global.root())) {
|
||||
XMLProperty * prop;
|
||||
if ((prop = root->property ("id"))) {
|
||||
set_id ((uint32_t) atoi (prop->value()));
|
||||
}
|
||||
if ((prop = root->property ("name"))) {
|
||||
set_name (prop->value());
|
||||
}
|
||||
|
||||
XMLNode * instant_xml = get_instant_xml ();
|
||||
if (instant_xml) {
|
||||
XMLNode * instant_copy = new XMLNode (*instant_xml);
|
||||
set_local_state (*instant_copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExportProfileManager::Preset::~Preset ()
|
||||
{
|
||||
if (local) {
|
||||
delete local;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::Preset::set_name (string name)
|
||||
{
|
||||
_name = name;
|
||||
|
||||
XMLNode * node;
|
||||
if ((node = global.root())) {
|
||||
node->add_property ("name", name);
|
||||
}
|
||||
if (local) {
|
||||
local->add_property ("name", name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::Preset::set_id (uint32_t id)
|
||||
{
|
||||
_id = id;
|
||||
|
||||
XMLNode * node;
|
||||
if ((node = global.root())) {
|
||||
node->add_property ("id", id);
|
||||
}
|
||||
if (local) {
|
||||
local->add_property ("id", id);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::Preset::set_global_state (XMLNode & state)
|
||||
{
|
||||
delete global.root ();
|
||||
global.set_root (&state);
|
||||
|
||||
set_id (_id);
|
||||
set_name (_name);
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::Preset::set_local_state (XMLNode & state)
|
||||
{
|
||||
delete local;
|
||||
local = &state;
|
||||
|
||||
set_id (_id);
|
||||
set_name (_name);
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::Preset::save () const
|
||||
{
|
||||
save_instant_xml ();
|
||||
if (global.root()) {
|
||||
global.write ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::Preset::remove_local () const
|
||||
{
|
||||
remove_instant_xml ();
|
||||
}
|
||||
|
||||
XMLNode *
|
||||
ExportProfileManager::Preset::get_instant_xml () const
|
||||
{
|
||||
XMLNode * instant_xml;
|
||||
|
||||
if ((instant_xml = session.instant_xml ("ExportPresets"))) {
|
||||
XMLNodeList children = instant_xml->children ("ExportPreset");
|
||||
for (XMLNodeList::iterator it = children.begin(); it != children.end(); ++it) {
|
||||
XMLProperty * prop;
|
||||
if ((prop = (*it)->property ("id")) && _id == (uint32_t) atoi (prop->value())) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::Preset::save_instant_xml () const
|
||||
{
|
||||
if (!local) { return; }
|
||||
|
||||
/* First remove old, then add new */
|
||||
|
||||
remove_instant_xml ();
|
||||
|
||||
XMLNode * instant_xml;
|
||||
if ((instant_xml = session.instant_xml ("ExportPresets"))) {
|
||||
instant_xml->add_child_copy (*local);
|
||||
} else {
|
||||
instant_xml = new XMLNode ("ExportPresets");
|
||||
instant_xml->add_child_copy (*local);
|
||||
session.add_instant_xml (*instant_xml, false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::Preset::remove_instant_xml () const
|
||||
{
|
||||
XMLNode * instant_xml;
|
||||
if ((instant_xml = session.instant_xml ("ExportPresets"))) {
|
||||
instant_xml->remove_nodes_and_delete ("id", to_string (_id, std::dec));
|
||||
}
|
||||
}
|
||||
|
||||
ExportProfileManager::ExportProfileManager (Session & s) :
|
||||
handler (s.get_export_handler()),
|
||||
session (s),
|
||||
|
||||
session_range (new Location ()),
|
||||
ranges (new LocationList ()),
|
||||
|
||||
format_list (new FormatList ())
|
||||
{
|
||||
|
||||
/* Initialize path variables */
|
||||
|
||||
sys::path path;
|
||||
|
||||
export_config_dir = user_config_directory();
|
||||
export_config_dir /= "export";
|
||||
search_path += export_config_dir;
|
||||
|
||||
path = ardour_search_path().to_string();
|
||||
path /= "export";
|
||||
search_path += path;
|
||||
|
||||
path = system_config_search_path().to_string();
|
||||
path /= "export";
|
||||
search_path += path;
|
||||
|
||||
/* create export config directory if necessary */
|
||||
|
||||
if (!sys::exists (export_config_dir)) {
|
||||
sys::create_directory (export_config_dir);
|
||||
}
|
||||
|
||||
load_presets ();
|
||||
load_formats ();
|
||||
}
|
||||
|
||||
ExportProfileManager::~ExportProfileManager ()
|
||||
{
|
||||
XMLNode * instant_xml (new XMLNode ("ExportProfile"));
|
||||
serialize_profile (*instant_xml);
|
||||
session.add_instant_xml (*instant_xml, false);
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::load_profile ()
|
||||
{
|
||||
XMLNode * instant_node = session.instant_xml ("ExportProfile");
|
||||
if (instant_node) {
|
||||
set_state (*instant_node);
|
||||
} else {
|
||||
XMLNode empty_node ("ExportProfile");
|
||||
set_state (empty_node);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::prepare_for_export ()
|
||||
{
|
||||
ChannelConfigPtr channel_config = channel_configs.front()->config;
|
||||
TimespanListPtr ts_list = timespans.front()->timespans;
|
||||
|
||||
FormatStateList::const_iterator format_it;
|
||||
FilenameStateList::const_iterator filename_it;
|
||||
|
||||
for (TimespanList::iterator ts_it = ts_list->begin(); ts_it != ts_list->end(); ++ts_it) {
|
||||
for (format_it = formats.begin(), filename_it = filenames.begin();
|
||||
format_it != formats.end() && filename_it != filenames.end();
|
||||
++format_it, ++filename_it) {
|
||||
|
||||
// filename->include_timespan = (ts_list->size() > 1); Disabled for now...
|
||||
handler->add_export_config (*ts_it, channel_config, (*format_it)->format, (*filename_it)->filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::load_preset (PresetPtr preset)
|
||||
{
|
||||
current_preset = preset;
|
||||
if (!preset) { return; }
|
||||
|
||||
XMLNode const * state;
|
||||
if ((state = preset->get_local_state())) {
|
||||
set_local_state (*state);
|
||||
}
|
||||
|
||||
if ((state = preset->get_global_state())) {
|
||||
set_global_state (*state);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::load_presets ()
|
||||
{
|
||||
preset_id_counter = 0;
|
||||
|
||||
vector<sys::path> found = find_file ("*.preset");
|
||||
|
||||
for (vector<sys::path>::iterator it = found.begin(); it != found.end(); ++it) {
|
||||
preset_id_counter = std::max (preset_id_counter, load_preset_from_disk (*it));
|
||||
}
|
||||
}
|
||||
|
||||
ExportProfileManager::PresetPtr
|
||||
ExportProfileManager::save_preset (string const & name)
|
||||
{
|
||||
if (!current_preset) {
|
||||
++preset_id_counter;
|
||||
string filename = export_config_dir.to_string() + "/" + to_string (preset_id_counter, std::dec) + ".preset";
|
||||
current_preset.reset (new Preset (filename, session));
|
||||
preset_list.push_back (current_preset);
|
||||
current_preset->set_id (preset_id_counter);
|
||||
}
|
||||
|
||||
XMLNode * global_preset = new XMLNode ("ExportPreset");
|
||||
XMLNode * local_preset = new XMLNode ("ExportPreset");
|
||||
|
||||
serialize_global_profile (*global_preset);
|
||||
serialize_local_profile (*local_preset);
|
||||
|
||||
current_preset->set_name (name);
|
||||
current_preset->set_global_state (*global_preset);
|
||||
current_preset->set_local_state (*local_preset);
|
||||
|
||||
current_preset->save();
|
||||
|
||||
return current_preset;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::remove_preset ()
|
||||
{
|
||||
if (!current_preset) { return; }
|
||||
|
||||
for (PresetList::iterator it = preset_list.begin(); it != preset_list.end(); ++it) {
|
||||
if (*it == current_preset) {
|
||||
preset_list.erase (it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FileMap::iterator it = preset_file_map.find (current_preset->id());
|
||||
if (it != preset_file_map.end()) {
|
||||
sys::remove (it->second);
|
||||
preset_file_map.erase (it);
|
||||
}
|
||||
|
||||
current_preset->remove_local();
|
||||
current_preset.reset();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ExportProfileManager::load_preset_from_disk (PBD::sys::path const & path)
|
||||
{
|
||||
PresetPtr preset (new Preset (path.to_string(), session));
|
||||
preset_list.push_back (preset);
|
||||
|
||||
/* Handle id to filename mapping */
|
||||
|
||||
FilePair pair (preset->id(), path);
|
||||
preset_file_map.insert (pair);
|
||||
|
||||
return preset->id();
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::set_state (XMLNode const & root)
|
||||
{
|
||||
set_global_state (root);
|
||||
set_local_state (root);
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::set_global_state (XMLNode const & root)
|
||||
{
|
||||
init_formats (root.children ("ExportFormat"));
|
||||
init_filenames (root.children ("ExportFilename"));
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::set_local_state (XMLNode const & root)
|
||||
{
|
||||
init_timespans (root.children ("ExportTimespan"));;
|
||||
init_channel_configs (root.children ("ExportChannelConfiguration"));
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::serialize_profile (XMLNode & root)
|
||||
{
|
||||
serialize_local_profile (root);
|
||||
serialize_global_profile (root);
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::serialize_global_profile (XMLNode & root)
|
||||
{
|
||||
for (FormatStateList::iterator it = formats.begin(); it != formats.end(); ++it) {
|
||||
root.add_child_nocopy (serialize_format (*it));
|
||||
}
|
||||
|
||||
for (FilenameStateList::iterator it = filenames.begin(); it != filenames.end(); ++it) {
|
||||
root.add_child_nocopy (serialize_filename (*it));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::serialize_local_profile (XMLNode & root)
|
||||
{
|
||||
for (TimespanStateList::iterator it = timespans.begin(); it != timespans.end(); ++it) {
|
||||
root.add_child_nocopy (serialize_timespan (*it));
|
||||
}
|
||||
|
||||
for (ChannelConfigStateList::iterator it = channel_configs.begin(); it != channel_configs.end(); ++it) {
|
||||
root.add_child_nocopy (serialize_channel_config (*it));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<sys::path>
|
||||
ExportProfileManager::find_file (std::string const & pattern)
|
||||
{
|
||||
vector<sys::path> found;
|
||||
|
||||
Glib::PatternSpec pattern_spec (pattern);
|
||||
find_matching_files_in_search_path (search_path, pattern_spec, found);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::set_selection_range (nframes_t start, nframes_t end)
|
||||
{
|
||||
|
||||
if (start || end) {
|
||||
selection_range.reset (new Location());
|
||||
selection_range->set_name (_("Selection"));
|
||||
selection_range->set (start, end);
|
||||
} else {
|
||||
selection_range.reset();
|
||||
}
|
||||
|
||||
for (TimespanStateList::iterator it = timespans.begin(); it != timespans.end(); ++it) {
|
||||
(*it)->selection_range = selection_range;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::init_timespans (XMLNodeList nodes)
|
||||
{
|
||||
timespans.clear ();
|
||||
|
||||
if (nodes.empty()) {
|
||||
update_ranges ();
|
||||
|
||||
TimespanStatePtr timespan (new TimespanState (session_range, selection_range, ranges));
|
||||
|
||||
timespans.push_back (timespan);
|
||||
return;
|
||||
}
|
||||
|
||||
for (XMLNodeList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
|
||||
timespans.push_back (deserialize_timespan (**it));
|
||||
}
|
||||
}
|
||||
|
||||
ExportProfileManager::TimespanStatePtr
|
||||
ExportProfileManager::deserialize_timespan (XMLNode & root)
|
||||
{
|
||||
TimespanStatePtr state (new TimespanState (session_range, selection_range, ranges));
|
||||
XMLProperty const * prop;
|
||||
|
||||
update_ranges ();
|
||||
|
||||
XMLNodeList spans = root.children ("Range");
|
||||
for (XMLNodeList::iterator node_it = spans.begin(); node_it != spans.end(); ++node_it) {
|
||||
|
||||
prop = (*node_it)->property ("id");
|
||||
if (!prop) { continue; }
|
||||
ustring id = prop->value();
|
||||
|
||||
for (LocationList::iterator it = ranges->begin(); it != ranges->end(); ++it) {
|
||||
if ((!id.compare ("session") && *it == session_range.get()) ||
|
||||
(!id.compare ("selection") && *it == selection_range.get()) ||
|
||||
(!id.compare ((*it)->id().to_s()))) {
|
||||
TimespanPtr timespan = handler->add_timespan();
|
||||
timespan->set_name ((*it)->name());
|
||||
timespan->set_range_id (id);
|
||||
timespan->set_range ((*it)->start(), (*it)->end());
|
||||
state->timespans->push_back (timespan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((prop = root.property ("format"))) {
|
||||
state->time_format = (TimeFormat) string_2_enum (prop->value(), TimeFormat);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
XMLNode &
|
||||
ExportProfileManager::serialize_timespan (TimespanStatePtr state)
|
||||
{
|
||||
XMLNode & root = *(new XMLNode ("ExportTimespan"));
|
||||
XMLNode * span;
|
||||
|
||||
update_ranges ();
|
||||
|
||||
for (TimespanList::iterator it = state->timespans->begin(); it != state->timespans->end(); ++it) {
|
||||
if ((span = root.add_child ("Range"))) {
|
||||
span->add_property ("id", (*it)->range_id());
|
||||
}
|
||||
}
|
||||
|
||||
root.add_property ("format", enum_2_string (state->time_format));
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::update_ranges () {
|
||||
ranges->clear();
|
||||
|
||||
/* Session */
|
||||
|
||||
session_range->set_name (_("Session"));
|
||||
session_range->set (session.current_start_frame(), session.current_end_frame());
|
||||
ranges->push_back (session_range.get());
|
||||
|
||||
/* Selection */
|
||||
|
||||
if (selection_range) {
|
||||
ranges->push_back (selection_range.get());
|
||||
}
|
||||
|
||||
/* ranges */
|
||||
|
||||
LocationList const & list (session.locations()->list());
|
||||
for (LocationList::const_iterator it = list.begin(); it != list.end(); ++it) {
|
||||
if ((*it)->is_range_marker()) {
|
||||
ranges->push_back (*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::init_channel_configs (XMLNodeList nodes)
|
||||
{
|
||||
channel_configs.clear();
|
||||
|
||||
if (nodes.empty()) {
|
||||
ChannelConfigStatePtr config (new ChannelConfigState (handler->add_channel_config()));
|
||||
channel_configs.push_back (config);
|
||||
return;
|
||||
}
|
||||
|
||||
for (XMLNodeList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
|
||||
channel_configs.push_back (deserialize_channel_config (**it));
|
||||
}
|
||||
}
|
||||
|
||||
ExportProfileManager::ChannelConfigStatePtr
|
||||
ExportProfileManager::deserialize_channel_config (XMLNode & root)
|
||||
{
|
||||
|
||||
ChannelConfigStatePtr state (new ChannelConfigState (handler->add_channel_config()));
|
||||
XMLProperty const * prop;
|
||||
|
||||
if ((prop = root.property ("split"))) {
|
||||
state->config->set_split(!prop->value().compare ("true"));
|
||||
}
|
||||
|
||||
XMLNodeList channels = root.children ("Channel");
|
||||
for (XMLNodeList::iterator it = channels.begin(); it != channels.end(); ++it) {
|
||||
boost::shared_ptr<ExportChannel> channel (new ExportChannel ());
|
||||
|
||||
XMLNodeList ports = (*it)->children ("Port");
|
||||
for (XMLNodeList::iterator p_it = ports.begin(); p_it != ports.end(); ++p_it) {
|
||||
if ((prop = (*p_it)->property ("name"))) {
|
||||
channel->add_port (dynamic_cast<AudioPort *> (session.engine().get_port_by_name (prop->value())));
|
||||
}
|
||||
}
|
||||
|
||||
state->config->register_channel (channel);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
XMLNode &
|
||||
ExportProfileManager::serialize_channel_config (ChannelConfigStatePtr state)
|
||||
{
|
||||
XMLNode * root = new XMLNode ("ExportChannelConfiguration");
|
||||
XMLNode * channel;
|
||||
XMLNode * port_node;
|
||||
|
||||
root->add_property ("split", state->config->get_split() ? "true" : "false");
|
||||
root->add_property ("channels", to_string (state->config->get_n_chans(), std::dec));
|
||||
|
||||
uint32_t i = 1;
|
||||
ExportChannelConfiguration::ChannelList const & chan_list = state->config->get_channels();
|
||||
for (ExportChannelConfiguration::ChannelList::const_iterator c_it = chan_list.begin(); c_it != chan_list.end(); ++c_it) {
|
||||
channel = root->add_child ("Channel");
|
||||
if (!channel) { continue; }
|
||||
|
||||
channel->add_property ("number", to_string (i, std::dec));
|
||||
|
||||
for (ExportChannel::const_iterator p_it = (*c_it)->begin(); p_it != (*c_it)->end(); ++p_it) {
|
||||
if ((port_node = channel->add_child ("Port"))) {
|
||||
port_node->add_property ("name", (*p_it)->name());
|
||||
}
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return *root;
|
||||
}
|
||||
|
||||
ExportProfileManager::FormatStatePtr
|
||||
ExportProfileManager::duplicate_format_state (FormatStatePtr state)
|
||||
{
|
||||
/* Note: The pointer in the new FormatState should point to the same format spec
|
||||
as the original state's pointer. The spec itself should not be copied! */
|
||||
|
||||
FormatStatePtr format (new FormatState (format_list, state->format));
|
||||
formats.push_back (format);
|
||||
return format;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::remove_format_state (FormatStatePtr state)
|
||||
{
|
||||
for (FormatStateList::iterator it = formats.begin(); it != formats.end(); ++it) {
|
||||
if (*it == state) {
|
||||
formats.erase (it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sys::path
|
||||
ExportProfileManager::save_format_to_disk (FormatPtr format)
|
||||
{
|
||||
// TODO filename character stripping
|
||||
|
||||
/* Get filename for file */
|
||||
|
||||
Glib::ustring new_name = format->name();
|
||||
new_name += ".format";
|
||||
|
||||
sys::path new_path (export_config_dir);
|
||||
new_path /= new_name;
|
||||
|
||||
/* Check if format is on disk already */
|
||||
FileMap::iterator it;
|
||||
if ((it = format_file_map.find (format->id())) != format_file_map.end()) {
|
||||
/* Update data */
|
||||
{
|
||||
XMLTree tree (it->second.to_string());
|
||||
tree.set_root (&format->get_state());
|
||||
tree.write();
|
||||
}
|
||||
|
||||
/* Rename if necessary */
|
||||
|
||||
if (new_name.compare (it->second.leaf())) {
|
||||
sys::rename (it->second, new_path);
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Write new file */
|
||||
|
||||
XMLTree tree (new_path.to_string());
|
||||
tree.set_root (&format->get_state());
|
||||
tree.write();
|
||||
}
|
||||
|
||||
FormatListChanged ();
|
||||
return new_path;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::remove_format_profile (FormatPtr format)
|
||||
{
|
||||
for (FormatList::iterator it = format_list->begin(); it != format_list->end(); ++it) {
|
||||
if (*it == format) {
|
||||
format_list->erase (it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FileMap::iterator it = format_file_map.find (format->id());
|
||||
if (it != format_file_map.end()) {
|
||||
sys::remove (it->second);
|
||||
format_file_map.erase (it);
|
||||
}
|
||||
|
||||
FormatListChanged ();
|
||||
}
|
||||
|
||||
ExportProfileManager::FormatPtr
|
||||
ExportProfileManager::get_new_format (FormatPtr original)
|
||||
{
|
||||
FormatPtr format;
|
||||
if (original) {
|
||||
format.reset (new ExportFormatSpecification (*original));
|
||||
} else {
|
||||
format = handler->add_format();
|
||||
format->set_name ("empty format");
|
||||
}
|
||||
|
||||
sys::path path = save_format_to_disk (format);
|
||||
FilePair pair (format->id(), path);
|
||||
format_file_map.insert (pair);
|
||||
|
||||
format_list->push_back (format);
|
||||
FormatListChanged ();
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::init_formats (XMLNodeList nodes)
|
||||
{
|
||||
formats.clear();
|
||||
|
||||
if (nodes.empty()) {
|
||||
FormatStatePtr format (new FormatState (format_list, FormatPtr ()));
|
||||
formats.push_back (format);
|
||||
return;
|
||||
}
|
||||
|
||||
for (XMLNodeList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
|
||||
FormatStatePtr format;
|
||||
if ((format = deserialize_format (**it))) {
|
||||
formats.push_back (format);
|
||||
}
|
||||
}
|
||||
|
||||
if (formats.empty ()) {
|
||||
FormatStatePtr format (new FormatState (format_list, FormatPtr ()));
|
||||
formats.push_back (format);
|
||||
}
|
||||
}
|
||||
|
||||
ExportProfileManager::FormatStatePtr
|
||||
ExportProfileManager::deserialize_format (XMLNode & root)
|
||||
{
|
||||
XMLProperty * prop;
|
||||
uint32_t id = 0;
|
||||
|
||||
if ((prop = root.property ("id"))) {
|
||||
id = atoi (prop->value());
|
||||
}
|
||||
|
||||
for (FormatList::iterator it = format_list->begin(); it != format_list->end(); ++it) {
|
||||
if ((*it)->id() == id) {
|
||||
return FormatStatePtr (new FormatState (format_list, *it));
|
||||
}
|
||||
}
|
||||
|
||||
return FormatStatePtr ();
|
||||
}
|
||||
|
||||
XMLNode &
|
||||
ExportProfileManager::serialize_format (FormatStatePtr state)
|
||||
{
|
||||
XMLNode * root = new XMLNode ("ExportFormat");
|
||||
|
||||
string id = state->format ? to_string (state->format->id(), std::dec) : "0";
|
||||
root->add_property ("id", id);
|
||||
|
||||
return *root;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::load_formats ()
|
||||
{
|
||||
vector<sys::path> found = find_file ("*.format");
|
||||
|
||||
for (vector<sys::path>::iterator it = found.begin(); it != found.end(); ++it) {
|
||||
load_format_from_disk (*it);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::load_format_from_disk (PBD::sys::path const & path)
|
||||
{
|
||||
XMLTree const tree (path.to_string());
|
||||
FormatPtr format = handler->add_format (*tree.root());
|
||||
format_list->push_back (format);
|
||||
|
||||
/* Handle id to filename mapping */
|
||||
|
||||
FilePair pair (format->id(), path);
|
||||
format_file_map.insert (pair);
|
||||
|
||||
FormatListChanged ();
|
||||
}
|
||||
|
||||
ExportProfileManager::FilenameStatePtr
|
||||
ExportProfileManager::duplicate_filename_state (FilenameStatePtr state)
|
||||
{
|
||||
FilenameStatePtr filename (new FilenameState (handler->add_filename_copy (state->filename)));
|
||||
filenames.push_back (filename);
|
||||
return filename;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::remove_filename_state (FilenameStatePtr state)
|
||||
{
|
||||
for (FilenameStateList::iterator it = filenames.begin(); it != filenames.end(); ++it) {
|
||||
if (*it == state) {
|
||||
filenames.erase (it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::init_filenames (XMLNodeList nodes)
|
||||
{
|
||||
filenames.clear ();
|
||||
|
||||
if (nodes.empty()) {
|
||||
FilenameStatePtr filename (new FilenameState (handler->add_filename()));
|
||||
filenames.push_back (filename);
|
||||
return;
|
||||
}
|
||||
|
||||
for (XMLNodeList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
|
||||
filenames.push_back (deserialize_filename (**it));
|
||||
}
|
||||
}
|
||||
|
||||
ExportProfileManager::FilenameStatePtr
|
||||
ExportProfileManager::deserialize_filename (XMLNode & root)
|
||||
{
|
||||
FilenamePtr filename = handler->add_filename();
|
||||
filename->set_state (root);
|
||||
return FilenameStatePtr (new FilenameState (filename));
|
||||
}
|
||||
|
||||
XMLNode &
|
||||
ExportProfileManager::serialize_filename (FilenameStatePtr state)
|
||||
{
|
||||
return state->filename->get_state();
|
||||
}
|
||||
|
||||
boost::shared_ptr<ExportProfileManager::Warnings>
|
||||
ExportProfileManager::get_warnings ()
|
||||
{
|
||||
boost::shared_ptr<Warnings> warnings (new Warnings ());
|
||||
|
||||
ChannelConfigStatePtr channel_config_state = channel_configs.front();
|
||||
TimespanStatePtr timespan_state = timespans.front();
|
||||
|
||||
/*** Check "global" config ***/
|
||||
|
||||
TimespanListPtr timespans = timespan_state->timespans;
|
||||
ChannelConfigPtr channel_config = channel_config_state->config;
|
||||
|
||||
/* Check Timespans are not empty */
|
||||
|
||||
if (timespans->empty()) {
|
||||
warnings->errors.push_back (_("No timespan has been selected!"));
|
||||
}
|
||||
|
||||
/* Check channel config ports */
|
||||
|
||||
if (!channel_config->all_channels_have_ports ()) {
|
||||
warnings->warnings.push_back (_("Some channels are empty"));
|
||||
}
|
||||
|
||||
/*** Check files ***/
|
||||
|
||||
FormatStateList::const_iterator format_it;
|
||||
FilenameStateList::const_iterator filename_it;
|
||||
for (format_it = formats.begin(), filename_it = filenames.begin();
|
||||
format_it != formats.end() && filename_it != filenames.end();
|
||||
++format_it, ++filename_it) {
|
||||
check_config (warnings, timespan_state, channel_config_state, *format_it, *filename_it);
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
void
|
||||
ExportProfileManager::check_config (boost::shared_ptr<Warnings> warnings,
|
||||
TimespanStatePtr timespan_state,
|
||||
ChannelConfigStatePtr channel_config_state,
|
||||
FormatStatePtr format_state,
|
||||
FilenameStatePtr filename_state)
|
||||
{
|
||||
TimespanListPtr timespans = timespan_state->timespans;
|
||||
ChannelConfigPtr channel_config = channel_config_state->config;
|
||||
FormatPtr format = format_state->format;
|
||||
FilenamePtr filename = filename_state->filename;
|
||||
|
||||
/* Check format and maximum channel count */
|
||||
if (!format || !format->type()) {
|
||||
warnings->errors.push_back (_("No format selected!"));
|
||||
} else if (format->channel_limit() < channel_config->get_n_chans()) {
|
||||
warnings->errors.push_back
|
||||
(string_compose (_("%1 supports only %2 channels, but you have %3 channels in your channel configuration"),
|
||||
format->format_name(),
|
||||
format->channel_limit(),
|
||||
channel_config->get_n_chans()));
|
||||
}
|
||||
|
||||
if (!warnings->errors.empty()) { return; }
|
||||
|
||||
/* Check filenames */
|
||||
|
||||
// filename->include_timespan = (timespans->size() > 1); Disabled for now...
|
||||
|
||||
for (std::list<TimespanPtr>::iterator timespan_it = timespans->begin(); timespan_it != timespans->end(); ++timespan_it) {
|
||||
filename->set_timespan (*timespan_it);
|
||||
|
||||
if (channel_config->get_split()) {
|
||||
filename->include_channel = true;
|
||||
|
||||
for (uint32_t chan = 1; chan <= channel_config->get_n_chans(); ++chan) {
|
||||
filename->set_channel (chan);
|
||||
|
||||
Glib::ustring path = filename->get_path (format);
|
||||
|
||||
if (sys::exists (sys::path (path))) {
|
||||
warnings->conflicting_filenames.push_back (path);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
Glib::ustring path = filename->get_path (format);
|
||||
|
||||
if (sys::exists (sys::path (path))) {
|
||||
warnings->conflicting_filenames.push_back (path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace ARDOUR
|
||||
51
libs/ardour/export_status.cc
Normal file
51
libs/ardour/export_status.cc
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/export_status.h>
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
ExportStatus::ExportStatus ()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void
|
||||
ExportStatus::init ()
|
||||
{
|
||||
stop = false;
|
||||
running = false;
|
||||
_aborted = false;
|
||||
|
||||
stage = export_None;
|
||||
progress = 0.0;
|
||||
|
||||
total_timespans = 0;
|
||||
timespan = 0;
|
||||
|
||||
total_channel_configs = 0;
|
||||
channel_config = 0;
|
||||
|
||||
total_formats = 0;
|
||||
format = 0;
|
||||
}
|
||||
|
||||
} // namespace ARDOUR
|
||||
113
libs/ardour/export_timespan.cc
Normal file
113
libs/ardour/export_timespan.cc
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/export_timespan.h>
|
||||
|
||||
#include <ardour/export_channel_configuration.h>
|
||||
#include <ardour/export_filename.h>
|
||||
#include <ardour/export_file_io.h>
|
||||
#include <ardour/export_failed.h>
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
ExportTimespan::ExportTimespan (ExportStatus & status, nframes_t frame_rate) :
|
||||
status (status),
|
||||
start_frame (0),
|
||||
end_frame (0),
|
||||
position (0),
|
||||
frame_rate (frame_rate)
|
||||
{
|
||||
}
|
||||
|
||||
ExportTimespan::~ExportTimespan ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ExportTimespan::register_channel (ExportChannel const & channel)
|
||||
{
|
||||
TempFilePtr ptr (new ExportTempFile (1, frame_rate));
|
||||
ChannelFilePair pair (channel, ptr);
|
||||
filemap.insert (pair);
|
||||
}
|
||||
|
||||
void
|
||||
ExportTimespan::rewind ()
|
||||
{
|
||||
for (TempFileMap::iterator it = filemap.begin(); it != filemap.end(); ++it) {
|
||||
it->second->reset_read ();
|
||||
}
|
||||
}
|
||||
|
||||
nframes_t
|
||||
ExportTimespan::get_data (float * data, nframes_t frames, ExportChannel const & channel)
|
||||
{
|
||||
TempFileMap::iterator it = filemap.find (channel);
|
||||
if (it == filemap.end()) {
|
||||
throw ExportFailed (_("Export failed due to programming error"), _("Trying to get data from ExportTimespan for channel that was never registered!"));
|
||||
}
|
||||
|
||||
return it->second->read (data, frames);
|
||||
}
|
||||
|
||||
void
|
||||
ExportTimespan::set_range (nframes_t start, nframes_t end)
|
||||
{
|
||||
start_frame = start;
|
||||
position = start_frame;
|
||||
end_frame = end;
|
||||
}
|
||||
|
||||
int
|
||||
ExportTimespan::process (nframes_t frames)
|
||||
{
|
||||
status.stage = export_ReadTimespan;
|
||||
|
||||
/* update position */
|
||||
|
||||
nframes_t frames_to_read;
|
||||
|
||||
if (position + frames <= end_frame) {
|
||||
frames_to_read = frames;
|
||||
} else {
|
||||
frames_to_read = end_frame - position;
|
||||
status.stop = true;
|
||||
}
|
||||
|
||||
position += frames_to_read;
|
||||
status.progress = (float) (position - start_frame) / (end_frame - start_frame);
|
||||
|
||||
/* Read channels from ports and save to tempfiles */
|
||||
|
||||
float * data = new float[frames_to_read];
|
||||
|
||||
for (TempFileMap::iterator it = filemap.begin(); it != filemap.end(); ++it) {
|
||||
it->first.read_ports (data, frames_to_read);
|
||||
it->second->write (data, frames_to_read);
|
||||
}
|
||||
|
||||
delete [] data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
} // namespace ARDOUR
|
||||
349
libs/ardour/export_utilities.cc
Normal file
349
libs/ardour/export_utilities.cc
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
Copyright (C) 1999-2008 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.
|
||||
|
||||
*/
|
||||
|
||||
/* see gdither.cc for why we have to do this */
|
||||
|
||||
#define _ISOC9X_SOURCE 1
|
||||
#define _ISOC99_SOURCE 1
|
||||
#include <cmath>
|
||||
#undef _ISOC99_SOURCE
|
||||
#undef _ISOC9X_SOURCE
|
||||
#undef __USE_SVID
|
||||
#define __USE_SVID 1
|
||||
#include <cstdlib>
|
||||
#undef __USE_SVID
|
||||
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <float.h>
|
||||
|
||||
/* ...*/
|
||||
|
||||
#include <ardour/export_utilities.h>
|
||||
|
||||
#include <ardour/export_failed.h>
|
||||
#include <ardour/gdither.h>
|
||||
#include <ardour/dB.h>
|
||||
#include <pbd/failed_constructor.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace PBD;
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
/* SampleRateConverter */
|
||||
|
||||
SampleRateConverter::SampleRateConverter (uint32_t channels, nframes_t in_rate, nframes_t out_rate, int quality) :
|
||||
channels (channels),
|
||||
leftover_frames (0),
|
||||
data_in (0),
|
||||
leftover_data (0),
|
||||
data_out (0),
|
||||
data_out_size (0),
|
||||
src_state (0)
|
||||
{
|
||||
if (in_rate == out_rate) {
|
||||
active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
active = true;
|
||||
int err;
|
||||
|
||||
if ((src_state = src_new (quality, channels, &err)) == 0) {
|
||||
throw ExportFailed (string_compose (_("cannot initialize sample rate conversion: %1"), src_strerror (err)), "Cannot initialize sample rate conversion");
|
||||
}
|
||||
|
||||
src_data.src_ratio = out_rate / (double) in_rate;
|
||||
}
|
||||
|
||||
SampleRateConverter::~SampleRateConverter ()
|
||||
{
|
||||
if (src_state) {
|
||||
src_delete (src_state);
|
||||
}
|
||||
if (data_out) {
|
||||
delete [] data_out;
|
||||
}
|
||||
if (leftover_data) {
|
||||
free (leftover_data);
|
||||
}
|
||||
}
|
||||
|
||||
nframes_t
|
||||
SampleRateConverter::process (float * data, nframes_t frames)
|
||||
{
|
||||
if (!active) {
|
||||
// Just pass it on...
|
||||
return piped_to->write (data, frames);
|
||||
}
|
||||
|
||||
/* Manage memory */
|
||||
|
||||
nframes_t out_samples_max = (nframes_t) ceil (frames * src_data.src_ratio * channels);
|
||||
if (data_out_size < out_samples_max) {
|
||||
|
||||
free (data_out);
|
||||
data_out = new float[out_samples_max];
|
||||
src_data.data_out = data_out;
|
||||
|
||||
max_leftover_frames = 4 * frames;
|
||||
leftover_data = (float *) realloc (leftover_data, max_leftover_frames * channels * sizeof (float));
|
||||
if (!leftover_data) {
|
||||
throw ExportFailed (_("A memory allocation error occured during sample rate conversion"), "Samplerate conversion failed");
|
||||
}
|
||||
|
||||
data_out_size = out_samples_max;
|
||||
}
|
||||
|
||||
/* Do SRC */
|
||||
|
||||
data_in = data;
|
||||
frames_in = frames;
|
||||
|
||||
int err;
|
||||
int cnt = 0;
|
||||
nframes_t frames_out_total = 0;
|
||||
|
||||
do {
|
||||
src_data.output_frames = out_samples_max / channels;
|
||||
src_data.end_of_input = end_of_input;
|
||||
src_data.data_out = data_out;
|
||||
|
||||
if (leftover_frames > 0) {
|
||||
|
||||
/* input data will be in leftover_data rather than data_in */
|
||||
|
||||
src_data.data_in = leftover_data;
|
||||
|
||||
if (cnt == 0) {
|
||||
|
||||
/* first time, append new data from data_in into the leftover_data buffer */
|
||||
|
||||
memcpy (leftover_data + (leftover_frames * channels), data_in, frames_in * channels * sizeof(float));
|
||||
src_data.input_frames = frames_in + leftover_frames;
|
||||
} else {
|
||||
|
||||
/* otherwise, just use whatever is still left in leftover_data; the contents
|
||||
were adjusted using memmove() right after the last SRC call (see
|
||||
below)
|
||||
*/
|
||||
|
||||
src_data.input_frames = leftover_frames;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
src_data.data_in = data_in;
|
||||
src_data.input_frames = frames_in;
|
||||
|
||||
}
|
||||
|
||||
++cnt;
|
||||
|
||||
if ((err = src_process (src_state, &src_data)) != 0) {
|
||||
throw ExportFailed (_("an error occured during sample rate conversion"), string_compose ("an error occured during sample rate conversion: %1", src_strerror (err)));
|
||||
}
|
||||
|
||||
frames_out = src_data.output_frames_gen;
|
||||
leftover_frames = src_data.input_frames - src_data.input_frames_used;
|
||||
|
||||
if (leftover_frames > 0) {
|
||||
if (leftover_frames > max_leftover_frames) {
|
||||
error << _("warning, leftover frames overflowed, glitches might occur in output") << endmsg;
|
||||
leftover_frames = max_leftover_frames;
|
||||
}
|
||||
memmove (leftover_data, (char *) (src_data.data_in + (src_data.input_frames_used * channels)),
|
||||
leftover_frames * channels * sizeof(float));
|
||||
}
|
||||
|
||||
|
||||
nframes_t frames_written = piped_to->write (data_out, frames_out);
|
||||
if (frames_written < 0) {
|
||||
return frames_written;
|
||||
} else {
|
||||
frames_out_total += frames_written;
|
||||
}
|
||||
|
||||
} while (leftover_frames > frames_in);
|
||||
|
||||
|
||||
return frames_out_total;
|
||||
}
|
||||
|
||||
/* SampleFormatConverter */
|
||||
|
||||
template <typename TOut>
|
||||
SampleFormatConverter<TOut>::SampleFormatConverter (uint32_t channels, ExportFormatBase::DitherType type, int data_width_) :
|
||||
channels (channels),
|
||||
data_width (data_width_),
|
||||
dither (0),
|
||||
data_out_size (0),
|
||||
data_out (0),
|
||||
clip_floats (false)
|
||||
{
|
||||
if (data_width != 24) {
|
||||
data_width = sizeof (TOut) * 8;
|
||||
}
|
||||
|
||||
GDitherSize dither_size = GDitherFloat;
|
||||
|
||||
switch (data_width) {
|
||||
case 8:
|
||||
dither_size = GDither8bit;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
dither_size = GDither16bit;
|
||||
break;
|
||||
case 24:
|
||||
dither_size = GDither32bit;
|
||||
}
|
||||
|
||||
dither = gdither_new ((GDitherType) type, channels, dither_size, data_width);
|
||||
}
|
||||
|
||||
template <typename TOut>
|
||||
SampleFormatConverter<TOut>::~SampleFormatConverter ()
|
||||
{
|
||||
if (dither) {
|
||||
gdither_free (dither);
|
||||
}
|
||||
if (data_out) {
|
||||
delete data_out;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TOut>
|
||||
nframes_t
|
||||
SampleFormatConverter<TOut>::process (float * data, nframes_t frames)
|
||||
{
|
||||
/* Make sure we have enough memory allocated */
|
||||
|
||||
size_t data_size = channels * frames * sizeof (TOut);
|
||||
if (data_size > data_out_size) {
|
||||
free (data_out);
|
||||
data_out = new TOut[data_size];
|
||||
data_out_size = data_size;
|
||||
}
|
||||
|
||||
/* Do conversion */
|
||||
|
||||
if (data_width < 32) {
|
||||
for (uint32_t chn = 0; chn < channels; ++chn) {
|
||||
gdither_runf (dither, chn, frames, data, data_out);
|
||||
}
|
||||
} else {
|
||||
for (uint32_t chn = 0; chn < channels; ++chn) {
|
||||
|
||||
TOut * ob = data_out;
|
||||
const double int_max = (float) INT_MAX;
|
||||
const double int_min = (float) INT_MIN;
|
||||
|
||||
nframes_t i;
|
||||
for (nframes_t x = 0; x < frames; ++x) {
|
||||
i = chn + (x * channels);
|
||||
|
||||
if (data[i] > 1.0f) {
|
||||
ob[i] = static_cast<TOut> (INT_MAX);
|
||||
} else if (data[i] < -1.0f) {
|
||||
ob[i] = static_cast<TOut> (INT_MIN);
|
||||
} else {
|
||||
if (data[i] >= 0.0f) {
|
||||
ob[i] = lrintf (int_max * data[i]);
|
||||
} else {
|
||||
ob[i] = - lrintf (int_min * data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write forward */
|
||||
|
||||
return GraphSinkVertex<float, TOut>::piped_to->write (data_out, frames);
|
||||
}
|
||||
|
||||
template<>
|
||||
nframes_t
|
||||
SampleFormatConverter<float>::process (float * data, nframes_t frames)
|
||||
{
|
||||
if (clip_floats) {
|
||||
for (nframes_t x = 0; x < frames * channels; ++x) {
|
||||
if (data[x] > 1.0f) {
|
||||
data[x] = 1.0f;
|
||||
} else if (data[x] < -1.0f) {
|
||||
data[x] = -1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return piped_to->write (data, frames);
|
||||
}
|
||||
|
||||
template class SampleFormatConverter<short>;
|
||||
template class SampleFormatConverter<int>;
|
||||
template class SampleFormatConverter<float>;
|
||||
|
||||
/* Normalizer */
|
||||
|
||||
Normalizer::Normalizer (uint32_t channels, float target_dB) :
|
||||
channels (channels),
|
||||
enabled (false)
|
||||
{
|
||||
target = dB_to_coefficient (target_dB);
|
||||
|
||||
if (target == 1.0f) {
|
||||
/* do not normalize to precisely 1.0 (0 dBFS), to avoid making it appear
|
||||
that we may have clipped.
|
||||
*/
|
||||
target -= FLT_EPSILON;
|
||||
}
|
||||
}
|
||||
|
||||
Normalizer::~Normalizer ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
Normalizer::set_peak (float peak)
|
||||
{
|
||||
if (peak == 0.0f || peak == target) {
|
||||
/* don't even try */
|
||||
enabled = false;
|
||||
} else {
|
||||
enabled = true;
|
||||
gain = target / peak;
|
||||
}
|
||||
}
|
||||
|
||||
nframes_t
|
||||
Normalizer::process (float * data, nframes_t frames)
|
||||
{
|
||||
if (enabled) {
|
||||
for (nframes_t i = 0; i < (channels * frames); ++i) {
|
||||
data[i] *= gain;
|
||||
}
|
||||
}
|
||||
return piped_to->write (data, frames);
|
||||
}
|
||||
|
||||
};
|
||||
500
libs/ardour/session_metadata.cc
Normal file
500
libs/ardour/session_metadata.cc
Normal file
|
|
@ -0,0 +1,500 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Author: Sakari Bergen
|
||||
|
||||
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/session_metadata.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace ARDOUR;
|
||||
|
||||
|
||||
SessionMetadata::SessionMetadata ()
|
||||
{
|
||||
/*** General ***/
|
||||
map.insert (Property ("comment", ""));
|
||||
map.insert (Property ("copyright", ""));
|
||||
map.insert (Property ("isrc", ""));
|
||||
map.insert (Property ("year", ""));
|
||||
|
||||
/*** Title and friends ***/
|
||||
map.insert (Property ("grouping", ""));
|
||||
map.insert (Property ("title", ""));
|
||||
map.insert (Property ("subtitle", ""));
|
||||
|
||||
/*** People... ***/
|
||||
map.insert (Property ("artist", ""));
|
||||
map.insert (Property ("album_artist", ""));
|
||||
map.insert (Property ("lyricist", ""));
|
||||
map.insert (Property ("composer", ""));
|
||||
map.insert (Property ("conductor", ""));
|
||||
map.insert (Property ("remixer", ""));
|
||||
map.insert (Property ("arranger", ""));
|
||||
map.insert (Property ("engineer", ""));
|
||||
map.insert (Property ("producer", ""));
|
||||
map.insert (Property ("dj_mixer", ""));
|
||||
map.insert (Property ("mixer", ""));
|
||||
//map.insert (Property ("performers", "")); // Multiple values [instrument]
|
||||
|
||||
/*** Album info ***/
|
||||
map.insert (Property ("album", ""));
|
||||
map.insert (Property ("compilation", ""));
|
||||
map.insert (Property ("disc_subtitle", ""));
|
||||
map.insert (Property ("disc_number", ""));
|
||||
map.insert (Property ("total_discs", ""));
|
||||
map.insert (Property ("track_number", ""));
|
||||
map.insert (Property ("total_tracks", ""));
|
||||
|
||||
/*** Style ***/
|
||||
map.insert (Property ("genre", ""));
|
||||
//map.insert (Property ("mood", ""));
|
||||
//map.insert (Property ("bpm", ""));
|
||||
|
||||
/*** Other ***/
|
||||
//map.insert (Property ("lyrics", ""));
|
||||
//map.insert (Property ("media", ""));
|
||||
//map.insert (Property ("label", ""));
|
||||
//map.insert (Property ("barcode", ""));
|
||||
//map.insert (Property ("encoded_by", ""));
|
||||
//map.insert (Property ("catalog_number", ""));
|
||||
|
||||
/*** Sorting orders ***/
|
||||
//map.insert (Property ("album_sort", ""));
|
||||
//map.insert (Property ("album_artist_sort", ""));
|
||||
//map.insert (Property ("artist_sort", ""));
|
||||
//map.insert (Property ("title_sort", ""));
|
||||
}
|
||||
|
||||
SessionMetadata::~SessionMetadata ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
XMLNode *
|
||||
SessionMetadata::get_xml (const ustring & name)
|
||||
{
|
||||
ustring value = get_value (name);
|
||||
if (value.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
XMLNode val ("value", value);
|
||||
XMLNode * node = new XMLNode (name);
|
||||
node->add_child_copy (val);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::get_value (const ustring & name) const
|
||||
{
|
||||
PropertyMap::const_iterator it = map.find (name);
|
||||
if (it == map.end()) {
|
||||
// Should not be reached!
|
||||
std::cerr << "Programming error in SessionMetadata::get_value" << std::endl;
|
||||
return "";
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SessionMetadata::get_uint_value (const ustring & name) const
|
||||
{
|
||||
return atoi (get_value (name).c_str());
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_value (const ustring & name, const ustring & value)
|
||||
{
|
||||
PropertyMap::iterator it = map.find (name);
|
||||
if (it == map.end()) {
|
||||
// Should not be reached!
|
||||
std::cerr << "Programming error in SessionMetadata::set_value" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
it->second = value;
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_value (const ustring & name, uint32_t value)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << value;
|
||||
if (oss.str().compare("0")) {
|
||||
set_value (name, oss.str());
|
||||
} else {
|
||||
set_value (name, "");
|
||||
}
|
||||
}
|
||||
|
||||
/*** Serialization ***/
|
||||
XMLNode &
|
||||
SessionMetadata::get_state ()
|
||||
{
|
||||
XMLNode * node = new XMLNode ("Metadata");
|
||||
XMLNode * prop;
|
||||
|
||||
for (PropertyMap::const_iterator it = map.begin(); it != map.end(); ++it) {
|
||||
if ((prop = get_xml (it->first))) {
|
||||
node->add_child_nocopy (*prop);
|
||||
}
|
||||
}
|
||||
|
||||
return *node;
|
||||
}
|
||||
|
||||
int
|
||||
SessionMetadata::set_state (const XMLNode & state)
|
||||
{
|
||||
const XMLNodeList & children = state.children();
|
||||
ustring name;
|
||||
ustring value;
|
||||
XMLNode * node;
|
||||
|
||||
for (XMLNodeConstIterator it = children.begin(); it != children.end(); it++) {
|
||||
node = *it;
|
||||
if (node->children().empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
name = node->name();
|
||||
node = *node->children().begin();
|
||||
value = node->content();
|
||||
|
||||
set_value (name, value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*** Accessing ***/
|
||||
ustring
|
||||
SessionMetadata::comment () const
|
||||
{
|
||||
return get_value("comment");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::copyright () const
|
||||
{
|
||||
return get_value("copyright");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::isrc () const
|
||||
{
|
||||
return get_value("isrc");
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SessionMetadata::year () const
|
||||
{
|
||||
return get_uint_value("year");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::grouping () const
|
||||
{
|
||||
return get_value("grouping");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::title () const
|
||||
{
|
||||
return get_value("title");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::subtitle () const
|
||||
{
|
||||
return get_value("subtitle");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::artist () const
|
||||
{
|
||||
return get_value("artist");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::album_artist () const
|
||||
{
|
||||
return get_value("album_artist");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::lyricist () const
|
||||
{
|
||||
return get_value("lyricist");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::composer () const
|
||||
{
|
||||
return get_value("composer");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::conductor () const
|
||||
{
|
||||
return get_value("conductor");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::remixer () const
|
||||
{
|
||||
return get_value("remixer");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::arranger () const
|
||||
{
|
||||
return get_value("arranger");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::engineer () const
|
||||
{
|
||||
return get_value("engineer");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::producer () const
|
||||
{
|
||||
return get_value("producer");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::dj_mixer () const
|
||||
{
|
||||
return get_value("dj_mixer");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::mixer () const
|
||||
{
|
||||
return get_value("mixer");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::album () const
|
||||
{
|
||||
return get_value("album");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::compilation () const
|
||||
{
|
||||
return get_value("compilation");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::disc_subtitle () const
|
||||
{
|
||||
return get_value("disc_subtitle");
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SessionMetadata::disc_number () const
|
||||
{
|
||||
return get_uint_value("disc_number");
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SessionMetadata::total_discs () const
|
||||
{
|
||||
return get_uint_value("total_discs");
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SessionMetadata::track_number () const
|
||||
{
|
||||
return get_uint_value("track_number");
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SessionMetadata::total_tracks () const
|
||||
{
|
||||
return get_uint_value("total_tracks");
|
||||
}
|
||||
|
||||
ustring
|
||||
SessionMetadata::genre () const
|
||||
{
|
||||
return get_value("genre");
|
||||
}
|
||||
|
||||
/*** Editing ***/
|
||||
void
|
||||
SessionMetadata::set_comment (const ustring & v)
|
||||
{
|
||||
set_value ("comment", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_copyright (const ustring & v)
|
||||
{
|
||||
set_value ("copyright", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_isrc (const ustring & v)
|
||||
{
|
||||
set_value ("isrc", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_year (uint32_t v)
|
||||
{
|
||||
set_value ("year", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_grouping (const ustring & v)
|
||||
{
|
||||
set_value ("grouping", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_title (const ustring & v)
|
||||
{
|
||||
set_value ("title", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_subtitle (const ustring & v)
|
||||
{
|
||||
set_value ("subtitle", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_artist (const ustring & v)
|
||||
{
|
||||
set_value ("artist", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_album_artist (const ustring & v)
|
||||
{
|
||||
set_value ("album_artist", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_lyricist (const ustring & v)
|
||||
{
|
||||
set_value ("lyricist", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_composer (const ustring & v)
|
||||
{
|
||||
set_value ("composer", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_conductor (const ustring & v)
|
||||
{
|
||||
set_value ("conductor", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_remixer (const ustring & v)
|
||||
{
|
||||
set_value ("remixer", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_arranger (const ustring & v)
|
||||
{
|
||||
set_value ("arranger", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_engineer (const ustring & v)
|
||||
{
|
||||
set_value ("engineer", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_producer (const ustring & v)
|
||||
{
|
||||
set_value ("producer", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_dj_mixer (const ustring & v)
|
||||
{
|
||||
set_value ("dj_mixer", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_mixer (const ustring & v)
|
||||
{
|
||||
set_value ("mixer", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_album (const ustring & v)
|
||||
{
|
||||
set_value ("album", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_compilation (const ustring & v)
|
||||
{
|
||||
set_value ("compilation", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_disc_subtitle (const ustring & v)
|
||||
{
|
||||
set_value ("disc_subtitle", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_disc_number (uint32_t v)
|
||||
{
|
||||
set_value ("disc_number", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_total_discs (uint32_t v)
|
||||
{
|
||||
set_value ("total_discs", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_track_number (uint32_t v)
|
||||
{
|
||||
set_value ("track_number", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_total_tracks (uint32_t v)
|
||||
{
|
||||
set_value ("total_tracks", v);
|
||||
}
|
||||
|
||||
void
|
||||
SessionMetadata::set_genre (const ustring & v)
|
||||
{
|
||||
set_value ("genre", v);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue