diff options
Diffstat (limited to 'libs/ardour')
29 files changed, 809 insertions, 2891 deletions
diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index b22ca9790f..cf0eafe288 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -74,8 +74,8 @@ class AudioEngine : public SessionHandlePtr Glib::Mutex& process_lock() { return _process_lock; } - nframes_t frame_rate(); - nframes_t frames_per_cycle(); + nframes_t frame_rate() const; + nframes_t frames_per_cycle() const; size_t raw_buffer_size(DataType t); @@ -230,9 +230,9 @@ _ the regular process() call to session->process() is not made. bool session_remove_pending; bool _running; bool _has_run; - nframes_t _buffer_size; + mutable nframes_t _buffer_size; std::map<DataType,size_t> _raw_buffer_sizes; - nframes_t _frame_rate; + mutable nframes_t _frame_rate; /// number of frames between each check for changes in monitor input nframes_t monitor_check_interval; /// time of the last monitor check in frames diff --git a/libs/ardour/ardour/export.h b/libs/ardour/ardour/export.h deleted file mode 100644 index ee533d4c6d..0000000000 --- a/libs/ardour/ardour/export.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - Copyright (C) 2000-2007 Paul Davis - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef __ardour_export_h__ -#define __ardour_export_h__ - -#include <map> -#include <vector> -#include <string> - - -#include <sndfile.h> -#include <samplerate.h> - -#include "ardour/ardour.h" -#include "ardour/gdither.h" - -namespace ARDOUR -{ - class Port; - - typedef std::pair<Port *, uint32_t> PortChannelPair; - typedef std::map<uint32_t, std::vector<PortChannelPair> > ExportPortMap; - - struct ExportSpecification : public SF_INFO, public PBD::ScopedConnectionList { - - ExportSpecification(); - ~ExportSpecification (); - - void init (); - void clear (); - - int prepare (nframes_t blocksize, nframes_t frame_rate); - - int process (nframes_t nframes); - - /* set by the user */ - - std::string path; - nframes_t sample_rate; - - int src_quality; - SNDFILE* out; - uint32_t channels; - ExportPortMap port_map; - nframes_t start_frame; - nframes_t end_frame; - GDitherType dither_type; - bool do_freewheel; - - /* used exclusively during export */ - - nframes_t frame_rate; - GDither dither; - float* dataF; - float* dataF2; - float* leftoverF; - nframes_t leftover_frames; - nframes_t max_leftover_frames; - void* output_data; - nframes_t out_samples_max; - uint32_t sample_bytes; - uint32_t data_width; - - nframes_t total_frames; - SF_INFO sfinfo; - SRC_DATA src_data; - SRC_STATE* src_state; - nframes_t pos; - - PBD::ScopedConnection freewheel_connection; - - /* shared between UI thread and audio thread */ - - volatile float progress; /* audio thread sets this */ - volatile bool stop; /* UI sets this */ - volatile bool running; /* audio thread sets to false when export is done */ - - int status; - }; - -} // namespace ARDOUR - -#endif /* __ardour_export_h__ */ diff --git a/libs/ardour/ardour/export_channel.h b/libs/ardour/ardour/export_channel.h index 73e3406869..51ecabee25 100644 --- a/libs/ardour/ardour/export_channel.h +++ b/libs/ardour/ardour/export_channel.h @@ -25,6 +25,7 @@ #include <boost/signals2.hpp> #include <boost/shared_ptr.hpp> +#include <boost/operators.hpp> #include "ardour/audioregion.h" #include "ardour/buffer_set.h" @@ -36,7 +37,7 @@ class AudioTrack; class AudioPort; /// Export channel base class interface for different source types -class ExportChannel +class ExportChannel : public boost::less_than_comparable<ExportChannel> { public: @@ -57,6 +58,7 @@ class ExportChannel /// Safe pointer for storing ExportChannels in ordered STL containers class ExportChannelPtr : public boost::shared_ptr<ExportChannel> + , public boost::less_than_comparable<ExportChannel> { public: ExportChannelPtr () {} diff --git a/libs/ardour/ardour/export_channel_configuration.h b/libs/ardour/ardour/export_channel_configuration.h index 9ca49f452d..4b027cc020 100644 --- a/libs/ardour/ardour/export_channel_configuration.h +++ b/libs/ardour/ardour/export_channel_configuration.h @@ -45,34 +45,15 @@ class Session; 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 (Session & session); public: + bool operator== (ExportChannelConfiguration const & other) const { return channels == other.channels; } + bool operator!= (ExportChannelConfiguration const & other) const { return channels != other.channels; } + XMLNode & get_state (); int set_state (const XMLNode &); @@ -89,40 +70,13 @@ class ExportChannelConfiguration uint32_t get_n_chans () const { return channels.size(); } void register_channel (ExportChannelPtr 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); - PBD::Signal0<void> FilesWritten; - - // Tells the handler the necessary information for it to handle tempfiles - void register_with_timespan (TimespanPtr timespan); - - void unregister_all (); - private: - typedef boost::shared_ptr<ExportStatus> ExportStatusPtr; - Session & session; - // 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; - ExportStatusPtr status; - - bool files_written; - - TimespanPtr timespan; ChannelList channels; - FileConfigList file_configs; - bool split; // Split to mono files Glib::ustring _name; }; diff --git a/libs/ardour/ardour/export_file_io.h b/libs/ardour/ardour/export_file_io.h deleted file mode 100644 index 48d5984f78..0000000000 --- a/libs/ardour/ardour/export_file_io.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - 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 <stdint.h> -#include <utility> - -#include <boost/shared_ptr.hpp> -#include <glibmm/ustring.h> -#include "ardour/sndfile_helpers.h" - -#include "ardour/graph.h" -#include "ardour/types.h" -#include "ardour/ardour.h" -#include "ardour/export_format_specification.h" -#include "ardour/export_utilities.h" - -namespace ARDOUR -{ - -/// Common part for all export file writers -class ExportFileWriter -{ - public: - virtual ~ExportFileWriter () {} - - std::string filename () const { return _filename; } - nframes_t position () const { return _position; } - - void set_position (nframes_t position) { _position = position; } - - protected: - ExportFileWriter (std::string filename) : _filename (filename) {} - - std::string _filename; - nframes_t _position; -}; - -/// Common interface for templated libsndfile writers -class SndfileWriterBase : public ExportFileWriter -{ - public: - - SNDFILE * get_sndfile () const { return sndfile; } - - protected: - SndfileWriterBase (int channels, nframes_t samplerate, int format, std::string const & path); - virtual ~SndfileWriterBase (); - - SF_INFO sf_info; - SNDFILE * sndfile; -}; - - -/// Template parameter specific parts of sndfile writer -template <typename T> -class SndfileWriter : public SndfileWriterBase, public GraphSink<T> -{ - // FIXME: having this protected doesn't work with Apple's gcc - // Should only be created vie ExportFileFactory and derived classes - public: // protected - friend class ExportFileFactory; - SndfileWriter (int channels, nframes_t samplerate, int format, std::string const & path); - - public: - nframes_t write (T * data, nframes_t frames); - virtual ~SndfileWriter () {} - - 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; - -}; - -class ExportFileFactory -{ - public: - typedef boost::shared_ptr<ExportFormatSpecification const> FormatPtr; - typedef GraphSink<float> FloatSink; - typedef boost::shared_ptr<FloatSink> FloatSinkPtr; - typedef boost::shared_ptr<ExportFileWriter> FileWriterPtr; - - typedef std::pair<FloatSinkPtr, FileWriterPtr> FilePair; - - static FilePair create (FormatPtr format, uint32_t channels, Glib::ustring const & filename); - static bool check (FormatPtr format, uint32_t channels); - - private: - - static FilePair create_sndfile (FormatPtr format, unsigned int channels, Glib::ustring const & filename); - static bool check_sndfile (FormatPtr format, unsigned int channels); -}; - -} // namespace ARDOUR - -#endif /* __ardour_export_file_io_h__ */ diff --git a/libs/ardour/ardour/export_format_base.h b/libs/ardour/ardour/export_format_base.h index dceb943e62..08bcbfb2bd 100644 --- a/libs/ardour/ardour/export_format_base.h +++ b/libs/ardour/ardour/export_format_base.h @@ -28,9 +28,11 @@ #include <sndfile.h> #include <samplerate.h> -#include "ardour/gdither_types.h" + #include "ardour/ardour.h" +#include "audiographer/sample_format_converter.h" + namespace ARDOUR { @@ -76,10 +78,10 @@ class ExportFormatBase { }; enum DitherType { - D_None = GDitherNone, - D_Rect = GDitherRect, - D_Tri = GDitherTri, - D_Shaped = GDitherShaped + D_None = AudioGrapher::D_None, + D_Rect = AudioGrapher::D_Rect, + D_Tri = AudioGrapher::D_Tri, + D_Shaped = AudioGrapher::D_Shaped }; enum Quality { diff --git a/libs/ardour/ardour/export_format_specification.h b/libs/ardour/ardour/export_format_specification.h index 628c70d25a..3b9382237c 100644 --- a/libs/ardour/ardour/export_format_specification.h +++ b/libs/ardour/ardour/export_format_specification.h @@ -126,6 +126,9 @@ class ExportFormatSpecification : public ExportFormatBase { nframes_t silence_beginning () const { return _silence_beginning.get_frames (sample_rate()); } nframes_t silence_end () const { return _silence_end.get_frames (sample_rate()); } + nframes_t silence_beginning (nframes_t samplerate) const { return _silence_beginning.get_frames (samplerate); } + nframes_t silence_end (nframes_t samplerate) const { return _silence_end.get_frames (samplerate); } + AnyTime silence_beginning_time () const { return _silence_beginning; } AnyTime silence_end_time () const { return _silence_end; } diff --git a/libs/ardour/ardour/export_graph_builder.h b/libs/ardour/ardour/export_graph_builder.h new file mode 100644 index 0000000000..1244afd647 --- /dev/null +++ b/libs/ardour/ardour/export_graph_builder.h @@ -0,0 +1,225 @@ +/* + Copyright (C) 2009 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_graph_builder_h__ +#define __ardour_export_graph_builder_h__ + +#include "ardour/ardour.h" +#include "ardour/export_handler.h" +#include "ardour/export_channel.h" +#include "ardour/export_format_base.h" + +#include "audiographer/identity_vertex.h" + +#include <glibmm/threadpool.h> + +namespace AudioGrapher { + class SampleRateConverter; + class PeakReader; + class Normalizer; + template <typename T> class SampleFormatConverter; + template <typename T> class Interleaver; + template <typename T> class SndfileWriter; + template <typename T> class SilenceTrimmer; + template <typename T> class TmpFile; + template <typename T> class Threader; + template <typename T> class AllocatingProcessContext; +} + +namespace ARDOUR +{ + +class ExportGraphBuilder +{ + private: + typedef ExportHandler::FileSpec FileSpec; + typedef ExportElementFactory::FilenamePtr FilenamePtr; + + typedef boost::shared_ptr<AudioGrapher::Sink<Sample> > FloatSinkPtr; + typedef boost::shared_ptr<AudioGrapher::IdentityVertex<Sample> > IdentityVertexPtr; + typedef std::map<ExportChannelPtr, IdentityVertexPtr> ChannelMap; + + public: + + ExportGraphBuilder (Session const & session); + ~ExportGraphBuilder (); + + int process (nframes_t frames, bool last_cycle); + + void reset (); + void add_config (FileSpec const & config); + + private: + + class Encoder : public sigc::trackable { + public: + template <typename T> boost::shared_ptr<AudioGrapher::Sink<T> > init (FileSpec const & new_config); + void add_child (FileSpec const & new_config); + bool operator== (FileSpec const & other_config) const; + + static int get_real_format (FileSpec const & config); + + private: + typedef boost::shared_ptr<AudioGrapher::SndfileWriter<Sample> > FloatWriterPtr; + typedef boost::shared_ptr<AudioGrapher::SndfileWriter<int> > IntWriterPtr; + typedef boost::shared_ptr<AudioGrapher::SndfileWriter<short> > ShortWriterPtr; + + template<typename T> void init_writer (boost::shared_ptr<AudioGrapher::SndfileWriter<T> > & writer); + void copy_files (std::string orig_path); + + FileSpec config; + std::list<FilenamePtr> filenames; + + // Only one of these should be available at a time + FloatWriterPtr float_writer; + IntWriterPtr int_writer; + ShortWriterPtr short_writer; + }; + + // sample format converter + class SFC { + public: + // This constructor so that this can be constructed like a Normalizer + SFC (ExportGraphBuilder &) {} + FloatSinkPtr init (FileSpec const & new_config, nframes_t max_frames); + void add_child (FileSpec const & new_config); + bool operator== (FileSpec const & other_config) const; + + private: + typedef boost::shared_ptr<AudioGrapher::SampleFormatConverter<Sample> > FloatConverterPtr; + typedef boost::shared_ptr<AudioGrapher::SampleFormatConverter<int> > IntConverterPtr; + typedef boost::shared_ptr<AudioGrapher::SampleFormatConverter<short> > ShortConverterPtr; + + FileSpec config; + std::list<Encoder> children; + int data_width; + + // Only one of these should be available at a time + FloatConverterPtr float_converter; + IntConverterPtr int_converter; + ShortConverterPtr short_converter; + }; + + class Normalizer : public sigc::trackable { + public: + Normalizer (ExportGraphBuilder & parent) : parent (parent) {} + FloatSinkPtr init (FileSpec const & new_config, nframes_t max_frames); + void add_child (FileSpec const & new_config); + bool operator== (FileSpec const & other_config) const; + + private: + typedef boost::shared_ptr<AudioGrapher::PeakReader> PeakReaderPtr; + typedef boost::shared_ptr<AudioGrapher::Normalizer> NormalizerPtr; + typedef boost::shared_ptr<AudioGrapher::TmpFile<Sample> > TmpFilePtr; + typedef boost::shared_ptr<AudioGrapher::Threader<Sample> > ThreaderPtr; + typedef boost::shared_ptr<AudioGrapher::AllocatingProcessContext<Sample> > BufferPtr; + + void start_post_processing(); + void do_post_processing(); + + ExportGraphBuilder & parent; + + FileSpec config; + nframes_t max_frames_out; + + BufferPtr buffer; + PeakReaderPtr peak_reader; + TmpFilePtr tmp_file; + NormalizerPtr normalizer; + ThreaderPtr threader; + std::list<SFC> children; + }; + + // sample rate converter + class SRC { + public: + SRC (ExportGraphBuilder & parent) : parent (parent) {} + FloatSinkPtr init (FileSpec const & new_config, nframes_t max_frames); + void add_child (FileSpec const & new_config); + bool operator== (FileSpec const & other_config) const; + + private: + typedef boost::shared_ptr<AudioGrapher::SampleRateConverter> SRConverterPtr; + + template<typename T> + void add_child_to_list (FileSpec const & new_config, std::list<T> & list); + + ExportGraphBuilder & parent; + FileSpec config; + std::list<SFC> children; + std::list<Normalizer> normalized_children; + SRConverterPtr converter; + nframes_t max_frames_out; + }; + + // Silence trimmer + adder + class SilenceHandler { + public: + SilenceHandler (ExportGraphBuilder & parent) : parent (parent) {} + FloatSinkPtr init (FileSpec const & new_config, nframes_t max_frames); + void add_child (FileSpec const & new_config); + bool operator== (FileSpec const & other_config) const; + + private: + typedef boost::shared_ptr<AudioGrapher::SilenceTrimmer<Sample> > SilenceTrimmerPtr; + + ExportGraphBuilder & parent; + FileSpec config; + std::list<SRC> children; + SilenceTrimmerPtr silence_trimmer; + nframes_t max_frames_in; + }; + + // channel configuration + class ChannelConfig { + public: + ChannelConfig (ExportGraphBuilder & parent) : parent (parent) {} + void init (FileSpec const & new_config, ChannelMap & channel_map); + void add_child (FileSpec const & new_config); + bool operator== (FileSpec const & other_config) const; + + private: + typedef boost::shared_ptr<AudioGrapher::Interleaver<Sample> > InterleaverPtr; + + ExportGraphBuilder & parent; + FileSpec config; + std::list<SilenceHandler> children; + InterleaverPtr interleaver; + nframes_t max_frames; + }; + + Session const & session; + + // Roots for export processor trees + typedef std::list<ChannelConfig> ChannelConfigList; + ChannelConfigList channel_configs; + + // The sources of all data, each channel is read only once + ChannelMap channels; + + Sample * process_buffer; + nframes_t process_buffer_frames; + + Glib::ThreadPool thread_pool; +}; + +} // namespace ARDOUR + +#endif /* __ardour_export_graph_builder_h__ */ diff --git a/libs/ardour/ardour/export_handler.h b/libs/ardour/ardour/export_handler.h index bbf1f7e208..f7f3b863b9 100644 --- a/libs/ardour/ardour/export_handler.h +++ b/libs/ardour/ardour/export_handler.h @@ -27,8 +27,8 @@ #include <boost/shared_ptr.hpp> -#include "ardour/session.h" #include "ardour/ardour.h" +#include "ardour/session.h" #include "ardour/types.h" namespace ARDOUR @@ -38,11 +38,11 @@ class ExportTimespan; class ExportChannelConfiguration; class ExportFormatSpecification; class ExportFilename; -class ExportProcessor; +class ExportGraphBuilder; class ExportElementFactory { - protected: + public: typedef boost::shared_ptr<ExportTimespan> TimespanPtr; typedef boost::shared_ptr<ExportChannelConfiguration> ChannelConfigPtr; typedef boost::shared_ptr<ExportFormatSpecification> FormatPtr; @@ -70,29 +70,30 @@ class ExportElementFactory class ExportHandler : public ExportElementFactory { - private: - - /* Stuff for export configs - * The multimap maps timespans to file specifications - */ - + public: struct FileSpec { - - FileSpec (ChannelConfigPtr channel_config, FormatPtr format, FilenamePtr filename) : - channel_config (channel_config), - format (format), - filename (filename) - {} + FileSpec() {} + FileSpec (ChannelConfigPtr channel_config, FormatPtr format, FilenamePtr filename) + : channel_config (channel_config) + , format (format) + , filename (filename) + {} ChannelConfigPtr channel_config; FormatPtr format; FilenamePtr filename; }; + + private: + + /* Stuff for export configs + * The multimap maps timespans to file specifications + */ typedef std::pair<TimespanPtr, FileSpec> ConfigPair; typedef std::multimap<TimespanPtr, FileSpec> ConfigMap; - typedef boost::shared_ptr<ExportProcessor> ProcessorPtr; + typedef boost::shared_ptr<ExportGraphBuilder> GraphBuilderPtr; typedef boost::shared_ptr<ExportStatus> StatusPtr; private: @@ -112,16 +113,26 @@ class ExportHandler : public ExportElementFactory private: Session & session; - ProcessorPtr processor; + GraphBuilderPtr graph_builder; StatusPtr export_status; ConfigMap config_map; bool realtime; - PBD::ScopedConnection files_written_connection; - PBD::ScopedConnection export_read_finished_connection; - std::list<Glib::ustring> files_written; - void add_file (const Glib::ustring&); + /* Timespan management */ + + void start_timespan (); + int process_timespan (nframes_t frames); + void finish_timespan (); + + typedef std::pair<ConfigMap::iterator, ConfigMap::iterator> TimespanBounds; + TimespanPtr current_timespan; + TimespanBounds timespan_bounds; + + PBD::ScopedConnection process_connection; + sframes_t process_position; + + PBD::ScopedConnection export_read_finished_connection; /* CD Marker stuff */ @@ -141,13 +152,13 @@ class ExportHandler : public ExportElementFactory /* Track info */ uint32_t track_number; - nframes_t track_position; - nframes_t track_duration; - nframes_t track_start_frame; + sframes_t track_position; + sframes_t track_duration; + sframes_t track_start_frame; /* Index info */ uint32_t index_number; - nframes_t index_position; + sframes_t index_position; }; @@ -162,23 +173,10 @@ class ExportHandler : public ExportElementFactory 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); + void frames_to_cd_frames_string (char* buf, sframes_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; - PBD::ScopedConnection channel_config_connection; - }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/export_processor.h b/libs/ardour/ardour/export_processor.h deleted file mode 100644 index 493dd3231f..0000000000 --- a/libs/ardour/ardour/export_processor.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - 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" - -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 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 PBD::Signal1<void,const Glib::ustring&> WritingFile; - - private: - - void reset (); - - Session & session; - boost::shared_ptr<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__ */ diff --git a/libs/ardour/ardour/export_profile_manager.h b/libs/ardour/ardour/export_profile_manager.h index a29979460f..793ceac1f5 100644 --- a/libs/ardour/ardour/export_profile_manager.h +++ b/libs/ardour/ardour/export_profile_manager.h @@ -264,6 +264,9 @@ class ExportProfileManager ChannelConfigStatePtr channel_config_state, FormatStatePtr format_state, FilenameStatePtr filename_state); + + bool check_format (FormatPtr format, uint32_t channels); + bool check_sndfile_format (FormatPtr format, unsigned int channels); /* Utilities */ diff --git a/libs/ardour/ardour/export_timespan.h b/libs/ardour/ardour/export_timespan.h index 7b7ae7cd99..9053ace0f9 100644 --- a/libs/ardour/ardour/export_timespan.h +++ b/libs/ardour/ardour/export_timespan.h @@ -39,9 +39,6 @@ class ExportTempFile; class ExportTimespan { private: - typedef boost::shared_ptr<ExportTempFile> TempFilePtr; - typedef std::pair<ExportChannelPtr, TempFilePtr> ChannelFilePair; - typedef std::map<ExportChannelPtr, TempFilePtr> TempFileMap; typedef boost::shared_ptr<ExportStatus> ExportStatusPtr; private: @@ -57,20 +54,6 @@ class ExportTimespan Glib::ustring range_id () const { return _range_id; } void set_range_id (Glib::ustring range_id) { _range_id = range_id; } - /// Registers a channel to be read when export starts rolling - void register_channel (ExportChannelPtr 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, ExportChannelPtr channel); - - /// Reads data from each channel and writes to tempfile - int process (nframes_t frames); - - PBD::ScopedConnection 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; } @@ -85,8 +68,6 @@ class ExportTimespan nframes_t position; nframes_t frame_rate; - TempFileMap filemap; - Glib::ustring _name; Glib::ustring _range_id; diff --git a/libs/ardour/ardour/export_utilities.h b/libs/ardour/ardour/export_utilities.h deleted file mode 100644 index 5733ebb403..0000000000 --- a/libs/ardour/ardour/export_utilities.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - 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__ */ diff --git a/libs/ardour/ardour/gdither.h b/libs/ardour/ardour/gdither.h deleted file mode 100644 index 67efcc3583..0000000000 --- a/libs/ardour/ardour/gdither.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk> - * - * 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 GDITHER_H -#define GDITHER_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "gdither_types.h" - -/* Create and initialise a state structure, takes a dither type, a number of - * channels and a bit depth as input - * - * The Dither type is one of - * - * GDitherNone - straight nearest neighbour rounding. Theres no pressing - * reason to do this at 8 or 16 bit, but you might want to at 24, for some - * reason. At the lest it will save you writing int->float conversion code, - * which is arder than it sounds. - * - * GDitherRect - mathematically most accurate, lowest noise floor, but not - * that good for audio. It is the fastest though. - * - * GDitherTri - a happy medium between Rectangular and Shaped, reasonable - * noise floor, not too obvious, quite fast. - * - * GDitherShaped - should have the least audible impact, but has the highest - * noise floor, fairly CPU intensive. Not advisible if your going to apply - * any frequency manipulation afterwards. - * - * channels, sets the number of channels in the output data, output data will - * be written interleaved into the area given to gdither_run(). Set to 1 - * if you are not working with interleaved buffers. - * - * bit depth, sets the bit width of the output sample data, it can be one of: - * - * GDither8bit - 8 bit unsiged - * GDither16bit - 16 bit signed - * GDither32bit - 24+bits in upper bits of a 32 bit word - * GDitherFloat - IEEE floating point (32bits) - * GDitherDouble - Double precision IEEE floating point (64bits) - * - * dither_depth, set the number of bits before the signal will be truncated to, - * eg. 16 will produce an output stream with 16bits-worth of signal. Setting to - * zero or greater than the width of the output format will dither to the - * maximum precision allowed by the output format. - */ -GDither gdither_new(GDitherType type, uint32_t channels, - - GDitherSize bit_depth, int dither_depth); - -/* Frees memory used by gdither_new. - */ -void gdither_free(GDither s); - -/* Applies dithering to the supplied signal. - * - * channel is the channel number you are processing (0 - channles-1), length is - * the length of the input, in samples, x is the input samples (float), y is - * where the output samples will be written, it should have the approaprate - * type for the chosen bit depth - */ -void gdither_runf(GDither s, uint32_t channel, uint32_t length, - float *x, void *y); - -/* see gdither_runf, vut input argument is double format */ -void gdither_run(GDither s, uint32_t channel, uint32_t length, - double *x, void *y); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libs/ardour/ardour/gdither_types.h b/libs/ardour/ardour/gdither_types.h deleted file mode 100644 index bcc0097d7f..0000000000 --- a/libs/ardour/ardour/gdither_types.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk> - * - * 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 GDITHER_TYPES_H -#define GDITHER_TYPES_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - GDitherNone = 0, - GDitherRect, - GDitherTri, - GDitherShaped -} GDitherType; - -typedef enum { - GDither8bit = 8, - GDither16bit = 16, - GDither32bit = 32, - GDitherFloat = 25, - GDitherDouble = 54 -} GDitherSize; - -typedef void *GDither; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libs/ardour/ardour/gdither_types_internal.h b/libs/ardour/ardour/gdither_types_internal.h deleted file mode 100644 index 6cb0c48af9..0000000000 --- a/libs/ardour/ardour/gdither_types_internal.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk> - * - * 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 GDITHER_TYPES_H -#define GDITHER_TYPES_H - -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#define GDITHER_SH_BUF_SIZE 8 -#define GDITHER_SH_BUF_MASK 7 - -/* this must agree with whats in gdither_types.h */ -typedef enum { - GDitherNone = 0, - GDitherRect, - GDitherTri, - GDitherShaped -} GDitherType; - -typedef enum { - GDither8bit = 8, - GDither16bit = 16, - GDither32bit = 32, - GDitherFloat = 25, - GDitherDouble = 54 -} GDitherSize; - -typedef struct { - uint32_t phase; - float buffer[GDITHER_SH_BUF_SIZE]; -} GDitherShapedState; - -typedef struct GDither_s { - GDitherType type; - uint32_t channels; - uint32_t bit_depth; - uint32_t dither_depth; - float scale; - uint32_t post_scale; - float post_scale_fp; - float bias; - - int clamp_u; - - int clamp_l; - float *tri_state; - GDitherShapedState *shaped_state; -} *GDither; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libs/ardour/ardour/graph.h b/libs/ardour/ardour/graph.h deleted file mode 100644 index 5d6919f56e..0000000000 --- a/libs/ardour/ardour/graph.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - 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__ */ - diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 900f0893da..634d62cc0b 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -809,7 +809,7 @@ AudioEngine::disconnect (Port& port) } ARDOUR::nframes_t -AudioEngine::frame_rate () +AudioEngine::frame_rate () const { GET_PRIVATE_JACK_POINTER_RET (_jack,0); if (_frame_rate == 0) { @@ -827,7 +827,7 @@ AudioEngine::raw_buffer_size (DataType t) } ARDOUR::nframes_t -AudioEngine::frames_per_cycle () +AudioEngine::frames_per_cycle () const { GET_PRIVATE_JACK_POINTER_RET (_jack,0); if (_buffer_size == 0) { diff --git a/libs/ardour/export_channel_configuration.cc b/libs/ardour/export_channel_configuration.cc index 89a4952feb..cc68356d82 100644 --- a/libs/ardour/export_channel_configuration.cc +++ b/libs/ardour/export_channel_configuration.cc @@ -22,7 +22,6 @@ #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" @@ -43,15 +42,11 @@ namespace ARDOUR ExportChannelConfiguration::ExportChannelConfiguration (Session & session) : session (session), - writer_thread (*this), - status (session.get_export_status ()), - files_written (false), split (false) { } - XMLNode & ExportChannelConfiguration::get_state () { @@ -104,121 +99,4 @@ ExportChannelConfiguration::all_channels_have_ports () const 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 (X_("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) -{ - SessionEvent::create_per_thread_pool ("exporter events", 64); - - // cc can be trated like 'this' - WriterThread & cc (*((WriterThread *) arg)); - - try { - 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(); - } - } catch (ExportFailed & e) { - cc->status->abort (true); - } - - 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 diff --git a/libs/ardour/export_file_io.cc b/libs/ardour/export_file_io.cc deleted file mode 100644 index 4c39e5c907..0000000000 --- a/libs/ardour/export_file_io.cc +++ /dev/null @@ -1,445 +0,0 @@ -/* - 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 <string.h> - -#include "ardour/export_file_io.h" - -#include "ardour/export_failed.h" -#include "pbd/failed_constructor.h" - -#include "i18n.h" - -using namespace std; -using namespace Glib; -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 (X_("Invalid format given for SndfileWriter!")); - } - - if (path.length() == 0) { - throw ExportFailed (X_("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(X_("Cannot open output file \"%1\" for SndFileWriter (%2)"), path, errbuf)); - } - } else { - FILE * file; - if (!(file = tmpfile ())) { - throw ExportFailed (X_("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 (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 (X_("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); -} - -ExportFileFactory::FilePair -ExportFileFactory::create (FormatPtr format, uint32_t channels, ustring const & filename) -{ - switch (format->type()) { - case ExportFormatBase::T_Sndfile: - return create_sndfile (format, channels, filename); - - default: - throw ExportFailed (X_("Invalid format given for ExportFileFactory::create!")); - } -} - -bool -ExportFileFactory::check (FormatPtr format, uint32_t channels) -{ - switch (format->type()) { - case ExportFormatBase::T_Sndfile: - return check_sndfile (format, channels); - - default: - throw ExportFailed (X_("Invalid format given for ExportFileFactory::check!")); - } -} - -ExportFileFactory::FilePair -ExportFileFactory::create_sndfile (FormatPtr format, unsigned int channels, ustring const & filename) -{ - 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; - - 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)); - sfc->pipe_to (sfw); - - return std::make_pair (boost::static_pointer_cast<FloatSink> (sfc), boost::static_pointer_cast<ExportFileWriter> (sfw)); - - } 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)); - sfc->pipe_to (sfw); - - return std::make_pair (boost::static_pointer_cast<FloatSink> (sfc), boost::static_pointer_cast<ExportFileWriter> (sfw)); - - } - - 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)); - sfc->pipe_to (sfw); - - return std::make_pair (boost::static_pointer_cast<FloatSink> (sfc), boost::static_pointer_cast<ExportFileWriter> (sfw)); -} - -bool -ExportFileFactory::check_sndfile (FormatPtr format, unsigned int channels) -{ - SF_INFO sf_info; - sf_info.channels = channels; - sf_info.samplerate = format->sample_rate (); - sf_info.format = format->format_id () | format->sample_format (); - - return (sf_format_check (&sf_info) == SF_TRUE ? true : false); -} - -} // namespace ARDOUR diff --git a/libs/ardour/export_graph_builder.cc b/libs/ardour/export_graph_builder.cc new file mode 100644 index 0000000000..5b5ec16b36 --- /dev/null +++ b/libs/ardour/export_graph_builder.cc @@ -0,0 +1,413 @@ +#include "ardour/export_graph_builder.h" + +#include "audiographer/interleaver.h" +#include "audiographer/normalizer.h" +#include "audiographer/peak_reader.h" +#include "audiographer/process_context.h" +#include "audiographer/sample_format_converter.h" +#include "audiographer/sndfile_writer.h" +#include "audiographer/sr_converter.h" +#include "audiographer/silence_trimmer.h" +#include "audiographer/threader.h" +#include "audiographer/tmp_file.h" +#include "audiographer/utils.h" + +#include "ardour/audioengine.h" +#include "ardour/export_channel_configuration.h" +#include "ardour/export_filename.h" +#include "ardour/export_format_specification.h" +#include "ardour/sndfile_helpers.h" + +#include "pbd/filesystem.h" + +using namespace AudioGrapher; + +namespace ARDOUR { + +ExportGraphBuilder::ExportGraphBuilder (Session const & session) + : session (session) + , thread_pool (4) // FIXME thread amount to cores amount +{ + process_buffer_frames = session.engine().frames_per_cycle(); + process_buffer = new Sample[process_buffer_frames]; + + // TODO move and/or use global silent buffers + AudioGrapher::Utils::init_zeros<Sample> (process_buffer_frames); +} + +ExportGraphBuilder::~ExportGraphBuilder () +{ + delete [] process_buffer; + + // TODO see bove + AudioGrapher::Utils::free_resources(); +} + +int +ExportGraphBuilder::process (nframes_t frames, bool last_cycle) +{ + for (ChannelMap::iterator it = channels.begin(); it != channels.end(); ++it) { + it->first->read (process_buffer, process_buffer_frames); + ProcessContext<Sample> context(process_buffer, process_buffer_frames, 1); + if (last_cycle) { context.set_flag (ProcessContext<Sample>::EndOfInput); } + it->second->process (context); + } + + return 0; +} + +void +ExportGraphBuilder::reset () +{ + channel_configs.clear (); + channels.clear (); +} + +void +ExportGraphBuilder::add_config (FileSpec const & config) +{ + for (ChannelConfigList::iterator it = channel_configs.begin(); it != channel_configs.end(); ++it) { + if (*it == config) { + it->add_child (config); + return; + } + } + + // No duplicate channel config found, create new one + channel_configs.push_back (ChannelConfig (*this)); + ChannelConfig & c_config (channel_configs.back()); + c_config.init (config, channels); +} + +/* Encoder */ + +template <> +boost::shared_ptr<AudioGrapher::Sink<Sample> > +ExportGraphBuilder::Encoder::init (FileSpec const & new_config) +{ + config = new_config; + init_writer (float_writer); + return float_writer; +} + +template <> +boost::shared_ptr<AudioGrapher::Sink<int> > +ExportGraphBuilder::Encoder::init (FileSpec const & new_config) +{ + config = new_config; + init_writer (int_writer); + return int_writer; +} + +template <> +boost::shared_ptr<AudioGrapher::Sink<short> > +ExportGraphBuilder::Encoder::init (FileSpec const & new_config) +{ + config = new_config; + init_writer (short_writer); + return short_writer; +} + +void +ExportGraphBuilder::Encoder::add_child (FileSpec const & new_config) +{ + filenames.push_back (new_config.filename); +} + +bool +ExportGraphBuilder::Encoder::operator== (FileSpec const & other_config) const +{ + return get_real_format (config) == get_real_format (other_config); +} + +int +ExportGraphBuilder::Encoder::get_real_format (FileSpec const & config) +{ + ExportFormatSpecification & format = *config.format; + return format.format_id() | format.sample_format() | format.endianness(); +} + +template<typename T> +void +ExportGraphBuilder::Encoder::init_writer (boost::shared_ptr<AudioGrapher::SndfileWriter<T> > & writer) +{ + unsigned channels = config.channel_config->get_n_chans(); + int format = get_real_format (config); + Glib::ustring filename = config.filename->get_path (config.format); + + writer.reset (new AudioGrapher::SndfileWriter<T> (channels, config.format->sample_rate(), format, filename)); + writer->FileWritten.connect (sigc::mem_fun (*this, &ExportGraphBuilder::Encoder::copy_files)); +} + +void +ExportGraphBuilder::Encoder::copy_files (std::string orig_path) +{ + while (filenames.size()) { + FilenamePtr & filename = filenames.front(); + PBD::sys::copy_file (orig_path, filename->get_path (config.format).c_str()); + filenames.pop_front(); + } +} + +/* SFC */ + +ExportGraphBuilder::FloatSinkPtr +ExportGraphBuilder::SFC::init (FileSpec const & new_config, nframes_t max_frames) +{ + config = new_config; + data_width = sndfile_data_width (Encoder::get_real_format (config)); + unsigned channels = new_config.channel_config->get_n_chans(); + + if (data_width == 8 || data_width == 16) { + short_converter = ShortConverterPtr (new SampleFormatConverter<short> (channels)); + short_converter->init (max_frames, config.format->dither_type(), data_width); + add_child (config); + return short_converter; + } else if (data_width == 24 || data_width == 32) { + int_converter = IntConverterPtr (new SampleFormatConverter<int> (channels)); + int_converter->init (max_frames, config.format->dither_type(), data_width); + add_child (config); + return int_converter; + } else { + float_converter = FloatConverterPtr (new SampleFormatConverter<Sample> (channels)); + float_converter->init (max_frames, config.format->dither_type(), data_width); + add_child (config); + return float_converter; + } +} + +void +ExportGraphBuilder::SFC::add_child (FileSpec const & new_config) +{ + for (std::list<Encoder>::iterator it = children.begin(); it != children.end(); ++it) { + if (*it == new_config) { + it->add_child (new_config); + return; + } + } + + children.push_back (Encoder()); + Encoder & encoder = children.back(); + + if (data_width == 8 || data_width == 16) { + short_converter->add_output (encoder.init<short> (new_config)); + } else if (data_width == 24 || data_width == 32) { + int_converter->add_output (encoder.init<int> (new_config)); + } else { + float_converter->add_output (encoder.init<Sample> (new_config)); + } +} + +bool +ExportGraphBuilder::SFC::operator== (FileSpec const & other_config) const +{ + return config.format->sample_format() == other_config.format->sample_format(); +} + +/* Normalizer */ + +ExportGraphBuilder::FloatSinkPtr +ExportGraphBuilder::Normalizer::init (FileSpec const & new_config, nframes_t /*max_frames*/) +{ + config = new_config; + max_frames_out = 4086; // TODO good chunk size + + buffer.reset (new AllocatingProcessContext<Sample> (max_frames_out, config.channel_config->get_n_chans())); + peak_reader.reset (new PeakReader ()); + normalizer.reset (new AudioGrapher::Normalizer (config.format->normalize_target())); + threader.reset (new Threader<Sample> (parent.thread_pool)); + + normalizer->alloc_buffer (max_frames_out); + normalizer->add_output (threader); + + int format = ExportFormatBase::F_RAW | ExportFormatBase::SF_Float; + tmp_file.reset (new TmpFile<float> (config.channel_config->get_n_chans(), + config.format->sample_rate(), format)); + tmp_file->FileWritten.connect (sigc::hide (sigc::mem_fun (*this, &Normalizer::start_post_processing))); + + add_child (new_config); + + peak_reader->add_output (tmp_file); + return peak_reader; +} + +void +ExportGraphBuilder::Normalizer::add_child (FileSpec const & new_config) +{ + for (std::list<SFC>::iterator it = children.begin(); it != children.end(); ++it) { + if (*it == new_config) { + it->add_child (new_config); + return; + } + } + + children.push_back (SFC (parent)); + threader->add_output (children.back().init (new_config, max_frames_out)); +} + +bool +ExportGraphBuilder::Normalizer::operator== (FileSpec const & other_config) const +{ + return config.format->normalize() == other_config.format->normalize() && + config.format->normalize_target() == other_config.format->normalize_target(); +} + +void +ExportGraphBuilder::Normalizer::start_post_processing() +{ + normalizer->set_peak (peak_reader->get_peak()); + tmp_file->seek (0, SndfileReader<Sample>::SeekBeginning); + parent.thread_pool.push (sigc::mem_fun (*this, &Normalizer::do_post_processing)); +} + +void +ExportGraphBuilder::Normalizer::do_post_processing() +{ + while (tmp_file->read (*buffer) == buffer->frames()) { + normalizer->process (*buffer); + } +} + +/* SRC */ + +ExportGraphBuilder::FloatSinkPtr +ExportGraphBuilder::SRC::init (FileSpec const & new_config, nframes_t max_frames) +{ + config = new_config; + converter.reset (new SampleRateConverter (new_config.channel_config->get_n_chans())); + ExportFormatSpecification & format = *new_config.format; + converter->init (parent.session.nominal_frame_rate(), format.sample_rate(), format.src_quality()); + max_frames_out = converter->allocate_buffers (max_frames); + + add_child (new_config); + + return converter; +} + +void +ExportGraphBuilder::SRC::add_child (FileSpec const & new_config) +{ + if (new_config.format->normalize()) { + add_child_to_list (new_config, normalized_children); + } else { + add_child_to_list (new_config, children); + } +} + +template<typename T> +void +ExportGraphBuilder::SRC::add_child_to_list (FileSpec const & new_config, std::list<T> & list) +{ + for (typename std::list<T>::iterator it = list.begin(); it != list.end(); ++it) { + if (*it == new_config) { + it->add_child (new_config); + return; + } + } + + list.push_back (T (parent)); + converter->add_output (list.back().init (new_config, max_frames_out)); +} + +bool +ExportGraphBuilder::SRC::operator== (FileSpec const & other_config) const +{ + return config.format->sample_rate() == other_config.format->sample_rate(); +} + +/* SilenceHandler */ +ExportGraphBuilder::FloatSinkPtr +ExportGraphBuilder::SilenceHandler::init (FileSpec const & new_config, nframes_t max_frames) +{ + config = new_config; + max_frames_in = max_frames; + nframes_t sample_rate = parent.session.nominal_frame_rate(); + + silence_trimmer.reset (new SilenceTrimmer<Sample>()); + silence_trimmer->set_trim_beginning (config.format->trim_beginning()); + silence_trimmer->set_trim_end (config.format->trim_end()); + silence_trimmer->add_silence_to_beginning (config.format->silence_beginning(sample_rate)); + silence_trimmer->add_silence_to_end (config.format->silence_end(sample_rate)); + silence_trimmer->limit_output_size (max_frames_in); + + add_child (new_config); + + return silence_trimmer; +} + +void +ExportGraphBuilder::SilenceHandler::add_child (FileSpec const & new_config) +{ + for (std::list<SRC>::iterator it = children.begin(); it != children.end(); ++it) { + if (*it == new_config) { + it->add_child (new_config); + return; + } + } + + children.push_back (SRC (parent)); + silence_trimmer->add_output (children.back().init (new_config, max_frames_in)); +} + +bool +ExportGraphBuilder::SilenceHandler::operator== (FileSpec const & other_config) const +{ + ExportFormatSpecification & format = *config.format; + ExportFormatSpecification & other_format = *other_config.format; + return (format.trim_beginning() == other_format.trim_beginning()) && + (format.trim_end() == other_format.trim_end()) && + (format.silence_beginning() == other_format.silence_beginning()) && + (format.silence_end() == other_format.silence_end()); +} + +/* ChannelConfig */ + +void +ExportGraphBuilder::ChannelConfig::init (FileSpec const & new_config, ChannelMap & channel_map) +{ + typedef ExportChannelConfiguration::ChannelList ChannelList; + + config = new_config; + max_frames = parent.session.engine().frames_per_cycle(); + + interleaver.reset (new Interleaver<Sample> ()); + interleaver->init (new_config.channel_config->get_n_chans(), max_frames); + + ChannelList const & channel_list = config.channel_config->get_channels(); + unsigned chan = 0; + for (ChannelList::const_iterator it = channel_list.begin(); it != channel_list.end(); ++it, ++chan) { + ChannelMap::iterator map_it = channel_map.find (*it); + if (map_it == channel_map.end()) { + std::pair<ChannelMap::iterator, bool> result_pair = + channel_map.insert (std::make_pair (*it, IdentityVertexPtr (new IdentityVertex<Sample> ()))); + assert (result_pair.second); + map_it = result_pair.first; + } + map_it->second->add_output (interleaver->input (chan)); + } + + add_child (new_config); +} + +void +ExportGraphBuilder::ChannelConfig::add_child (FileSpec const & new_config) +{ + for (std::list<SilenceHandler>::iterator it = children.begin(); it != children.end(); ++it) { + if (*it == new_config) { + it->add_child (new_config); + return; + } + } + + children.push_back (SilenceHandler (parent)); + nframes_t max_frames_out = new_config.channel_config->get_n_chans() * max_frames; + interleaver->add_output (children.back().init (new_config, max_frames_out)); +} + +bool +ExportGraphBuilder::ChannelConfig::operator== (FileSpec const & other_config) const +{ + return config.channel_config == other_config.channel_config; +} + +} // namespace ARDOUR diff --git a/libs/ardour/export_handler.cc b/libs/ardour/export_handler.cc index b78fc20f7e..3ce07cf44d 100644 --- a/libs/ardour/export_handler.cc +++ b/libs/ardour/export_handler.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2008 Paul Davis + Copyright (C) 2008-2009 Paul Davis Author: Sakari Bergen This program is free software; you can redistribute it and/or modify @@ -27,12 +27,12 @@ #include "ardour/ardour.h" #include "ardour/configuration.h" +#include "ardour/export_graph_builder.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 std; @@ -98,33 +98,19 @@ ExportElementFactory::add_filename_copy (FilenamePtr other) /*** ExportHandler ***/ -ExportHandler::ExportHandler (Session & session) - : ExportElementFactory (session) - , session (session) - , export_status (session.get_export_status ()) - , realtime (false) -{ - processor.reset (new ExportProcessor (session)); - - ExportProcessor::WritingFile.connect_same_thread (files_written_connection, boost::bind (&ExportHandler::add_file, this, _1)); -} +ExportHandler::ExportHandler (Session & session) + : ExportElementFactory (session) + , session (session) + , graph_builder (new ExportGraphBuilder (session)) + , export_status (session.get_export_status ()) + , realtime (false) -ExportHandler::~ExportHandler () { - if (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(); } -void -ExportHandler::add_file (const Glib::ustring& str) +ExportHandler::~ExportHandler () { - files_written.push_back (str); + // TODO remove files that were written but not finsihed } bool @@ -137,21 +123,6 @@ ExportHandler::add_export_config (TimespanPtr timespan, ChannelConfigPtr channel 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 ExportReadFinished 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) { @@ -172,6 +143,73 @@ ExportHandler::do_export (bool rt) start_timespan (); } +void +ExportHandler::start_timespan () +{ + export_status->timespan++; + + if (config_map.empty()) { + export_status->running = false; + return; + } + + current_timespan = config_map.begin()->first; + + /* Register file configurations to graph builder */ + + timespan_bounds = config_map.equal_range (current_timespan); + graph_builder->reset (); + for (ConfigMap::iterator it = timespan_bounds.first; it != timespan_bounds.second; ++it) { + graph_builder->add_config (it->second); + } + + /* start export */ + + session.ProcessExport.connect_same_thread (process_connection, boost::bind (&ExportHandler::process_timespan, this, _1)); + process_position = current_timespan->get_start(); + session.start_audio_export (process_position, realtime); +} + +int +ExportHandler::process_timespan (nframes_t frames) +{ + /* update position */ + + nframes_t frames_to_read = 0; + sframes_t const start = current_timespan->get_start(); + sframes_t const end = current_timespan->get_end(); + + bool const last_cycle = (process_position + frames >= end); + + if (last_cycle) { + frames_to_read = end - process_position; + export_status->stop = true; + } else { + frames_to_read = frames; + } + + process_position += frames_to_read; + export_status->progress = (float) (process_position - start) / (end - start); + + /* Do actual processing */ + + return graph_builder->process (frames_to_read, last_cycle); +} + +void +ExportHandler::finish_timespan () +{ + process_connection.disconnect (); + + while (config_map.begin() != timespan_bounds.second) { + config_map.erase (config_map.begin()); + } + + start_timespan (); +} + +/*** CD Marker sutff ***/ + struct LocationSortByStart { bool operator() (Location *a, Location *b) { return a->start() < b->start(); @@ -242,7 +280,7 @@ ExportHandler::export_cd_marker_file (TimespanPtr timespan, FormatPtr file_forma /* Start actual marker stuff */ - nframes_t last_end_time = timespan->get_start(), last_start_time = timespan->get_start(); + sframes_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) { @@ -469,9 +507,9 @@ ExportHandler::write_index_info_toc (CDMarkerStatus & status) } void -ExportHandler::frames_to_cd_frames_string (char* buf, nframes_t when) +ExportHandler::frames_to_cd_frames_string (char* buf, sframes_t when) { - nframes_t remainder; + sframes_t remainder; nframes_t fr = session.nominal_frame_rate(); int mins, secs, frames; @@ -483,109 +521,4 @@ ExportHandler::frames_to_cd_frames_string (char* buf, nframes_t when) sprintf (buf, " %02d:%02d:%02d", mins, secs, frames); } -void -ExportHandler::start_timespan () -{ - export_status->timespan++; - - if (config_map.empty()) { - export_status->finish (); - 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 */ - - session.ProcessExport.connect_same_thread (current_timespan->process_connection, boost::bind (&ExportTimespan::process, current_timespan, _1)); - 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 */ - - export_status->total_formats = 0; - export_status->format = 0; - - for (ConfigMap::iterator it = timespan_bounds.first; it != timespan_bounds.second; ++it) { - - 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(); - export_read_finished_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; - } - - cc->FilesWritten.connect_same_thread (channel_config_connection, boost::bind (&ExportHandler::timespan_thread_finished, this)); - ++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 diff --git a/libs/ardour/export_processor.cc b/libs/ardour/export_processor.cc deleted file mode 100644 index c7fcbd55aa..0000000000 --- a/libs/ardour/export_processor.cc +++ /dev/null @@ -1,295 +0,0 @@ -/* - 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 "i18n.h" - -using namespace PBD; - -namespace ARDOUR -{ - -PBD::Signal1<void,const Glib::ustring&> ExportProcessor::WritingFile; - -ExportProcessor::ExportProcessor (Session & session) : - session (session), - status (session.get_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) -{ - 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); - } - - /* Ensure directory exists */ - - sys::path folder (filename->get_folder()); - if (!sys::exists (folder)) { - if (!sys::create_directory (folder)) { - throw ExportFailed (X_("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); - ExportFileFactory::FilePair pair = ExportFileFactory::create (format, 1, filename->get_path (format)); - file_sinks.push_back (pair.first); - writer_list.push_back (pair.second); - WritingFile (filename->get_path (format)); - } - - } else { - ExportFileFactory::FilePair pair = ExportFileFactory::create (format, channels, filename->get_path (format)); - file_sinks.push_back (pair.first); - writer_list.push_back (pair.second); - 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()); - } -} - -}; // namespace ARDOUR diff --git a/libs/ardour/export_profile_manager.cc b/libs/ardour/export_profile_manager.cc index 2b1b15dbce..244ec788ae 100644 --- a/libs/ardour/export_profile_manager.cc +++ b/libs/ardour/export_profile_manager.cc @@ -28,13 +28,13 @@ #include "pbd/convert.h" #include "ardour/export_profile_manager.h" -#include "ardour/export_file_io.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_preset.h" #include "ardour/export_handler.h" +#include "ardour/export_failed.h" #include "ardour/filename_extensions.h" #include "ardour/session.h" @@ -724,7 +724,7 @@ ExportProfileManager::check_config (boost::shared_ptr<Warnings> warnings, warnings->errors.push_back (_("No format selected!")); } else if (!channel_config->get_n_chans()) { warnings->errors.push_back (_("All channels are empty!")); - } else if (!ExportFileFactory::check (format, channel_config->get_n_chans())) { + } else if (!check_format (format, channel_config->get_n_chans())) { warnings->errors.push_back (_("One or more of the selected formats is not compatible with this system!")); } else if (format->channel_limit() < channel_config->get_n_chans()) { warnings->errors.push_back @@ -766,4 +766,27 @@ ExportProfileManager::check_config (boost::shared_ptr<Warnings> warnings, } } +bool +ExportProfileManager::check_format (FormatPtr format, uint32_t channels) +{ + switch (format->type()) { + case ExportFormatBase::T_Sndfile: + return check_sndfile_format (format, channels); + + default: + throw ExportFailed (X_("Invalid format given for ExportFileFactory::check!")); + } +} + +bool +ExportProfileManager::check_sndfile_format (FormatPtr format, unsigned int channels) +{ + SF_INFO sf_info; + sf_info.channels = channels; + sf_info.samplerate = format->sample_rate (); + sf_info.format = format->format_id () | format->sample_format (); + + return (sf_format_check (&sf_info) == SF_TRUE ? true : false); +} + }; // namespace ARDOUR diff --git a/libs/ardour/export_timespan.cc b/libs/ardour/export_timespan.cc index d638c84b16..948bf768d5 100644 --- a/libs/ardour/export_timespan.cc +++ b/libs/ardour/export_timespan.cc @@ -22,7 +22,6 @@ #include "ardour/export_channel_configuration.h" #include "ardour/export_filename.h" -#include "ardour/export_file_io.h" #include "ardour/export_failed.h" namespace ARDOUR @@ -42,33 +41,6 @@ ExportTimespan::~ExportTimespan () } void -ExportTimespan::register_channel (ExportChannelPtr 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, ExportChannelPtr channel) -{ - TempFileMap::iterator it = filemap.find (channel); - if (it == filemap.end()) { - throw ExportFailed (X_("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; @@ -76,38 +48,4 @@ ExportTimespan::set_range (nframes_t start, nframes_t end) 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 (data, frames_to_read); - it->second->write (data, frames_to_read); - } - - delete [] data; - - return 0; -} - - } // namespace ARDOUR diff --git a/libs/ardour/export_utilities.cc b/libs/ardour/export_utilities.cc deleted file mode 100644 index a91912359e..0000000000 --- a/libs/ardour/export_utilities.cc +++ /dev/null @@ -1,352 +0,0 @@ -/* - 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 <string.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), - max_leftover_frames (0), - frames_in (0), - frames_out(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 (X_("Cannot initialize sample rate conversion: %1"), src_strerror (err))); - } - - src_data.src_ratio = out_rate / (double) in_rate; -} - -SampleRateConverter::~SampleRateConverter () -{ - if (src_state) { - src_delete (src_state); - } - - 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) { - - delete[] 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 (X_("A memory allocation error occured during sample rate conversion")); - } - - 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 (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); - 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); - } - - 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) { - - delete[] 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); -} - -}; diff --git a/libs/ardour/gdither.cc b/libs/ardour/gdither.cc deleted file mode 100644 index f09e06edc2..0000000000 --- a/libs/ardour/gdither.cc +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk> - * - * 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/gdither_types_internal.h" -#include "ardour/gdither.h" -#include "ardour/noise.h" - -/* this monstrosity is necessary to get access to lrintf() and random(). - whoever is writing the glibc headers <cmath> and <cstdlib> should be - hauled off to a programmer re-education camp. for the rest of - their natural lives. or longer. <paul@linuxaudiosystems.com> -*/ - -#define _ISOC9X_SOURCE 1 -#define _ISOC99_SOURCE 1 -#ifdef __cplusplus -#include <cmath> -#else -#include <math.h> -#endif - -#undef __USE_SVID -#define __USE_SVID 1 -#ifdef __cplusplus -#include <cstdlib> -#else -#include <stdlib.h> -#endif - -#include <sys/types.h> - -/* Lipshitz's minimally audible FIR, only really works for 46kHz-ish signals */ -static const float shaped_bs[] = { 2.033f, -2.165f, 1.959f, -1.590f, 0.6149f }; - -/* Some useful constants */ -#define MAX_U8 255 -#define MIN_U8 0 -#define SCALE_U8 128.0f - -#define MAX_S16 32767 -#define MIN_S16 -32768 -#define SCALE_S16 32768.0f - -#define MAX_S24 8388607 -#define MIN_S24 -8388608 -#define SCALE_S24 8388608.0f - -GDither gdither_new(GDitherType type, uint32_t channels, - - GDitherSize bit_depth, int dither_depth) -{ - GDither s; - - s = (GDither)calloc(1, sizeof(struct GDither_s)); - s->type = type; - s->channels = channels; - s->bit_depth = (int)bit_depth; - - if (dither_depth <= 0 || dither_depth > (int)bit_depth) { - dither_depth = (int)bit_depth; - } - s->dither_depth = dither_depth; - - s->scale = (float)(1LL << (dither_depth - 1)); - if (bit_depth == GDitherFloat || bit_depth == GDitherDouble) { - s->post_scale_fp = 1.0f / s->scale; - s->post_scale = 0; - } else { - s->post_scale_fp = 0.0f; - s->post_scale = 1 << ((int)bit_depth - dither_depth); - } - - switch (bit_depth) { - case GDither8bit: - /* Unsigned 8 bit */ - s->bias = 1.0f; - s->clamp_u = 255; - s->clamp_l = 0; - break; - case GDither16bit: - /* Signed 16 bit */ - s->bias = 0.0f; - s->clamp_u = 32767; - s->clamp_l = -32768; - break; - case GDither32bit: - /* Signed 24 bit, in upper 24 bits of 32 bit word */ - s->bias = 0.0f; - s->clamp_u = 8388607; - s->clamp_l = -8388608; - break; - case GDitherFloat: - /* normalised float */ - s->bias = 0.0f; - s->clamp_u = lrintf(s->scale); - s->clamp_l = lrintf(-s->scale); - break; - case GDitherDouble: - /* normalised float */ - s->bias = 0.0f; - s->clamp_u = lrintf(s->scale); - s->clamp_l = lrintf(-s->scale); - break; - case 23: - /* special performance test case */ - s->scale = SCALE_S24; - s->post_scale = 256; - s->bias = 0.0f; - s->clamp_u = 8388607; - s->clamp_l = -8388608; - break; - default: - /* Not a bit depth we can handle */ - free(s); - - return NULL; - break; - } - - switch (type) { - case GDitherNone: - case GDitherRect: - /* No state */ - break; - - case GDitherTri: - /* The last whitenoise sample */ - s->tri_state = (float *) calloc(channels, sizeof(float)); - break; - - case GDitherShaped: - /* The error from the last few samples encoded */ - s->shaped_state = (GDitherShapedState*) - calloc(channels, sizeof(GDitherShapedState)); - break; - } - - return s; -} - -void gdither_free(GDither s) -{ - if (s) { - free(s->tri_state); - free(s->shaped_state); - free(s); - } -} - -inline static void gdither_innner_loop(const GDitherType dt, - const uint32_t stride, const float bias, const float scale, - - const uint32_t post_scale, const int bit_depth, - const uint32_t channel, const uint32_t length, float *ts, - - GDitherShapedState *ss, float *x, void *y, const int clamp_u, - - const int clamp_l) -{ - uint32_t pos, i; - uint8_t *o8 = (uint8_t*) y; - int16_t *o16 = (int16_t*) y; - int32_t *o32 = (int32_t*) y; - float tmp, r, ideal; - int64_t clamped; - - i = channel; - for (pos = 0; pos < length; pos++, i += stride) { - tmp = x[i] * scale + bias; - - switch (dt) { - case GDitherNone: - break; - case GDitherRect: - tmp -= GDITHER_NOISE; - break; - case GDitherTri: - r = GDITHER_NOISE - 0.5f; - tmp -= r - ts[channel]; - ts[channel] = r; - break; - case GDitherShaped: - /* Save raw value for error calculations */ - ideal = tmp; - - /* Run FIR and add white noise */ - ss->buffer[ss->phase] = GDITHER_NOISE * 0.5f; - tmp += ss->buffer[ss->phase] * shaped_bs[0] - + ss->buffer[(ss->phase - 1) & GDITHER_SH_BUF_MASK] - * shaped_bs[1] - + ss->buffer[(ss->phase - 2) & GDITHER_SH_BUF_MASK] - * shaped_bs[2] - + ss->buffer[(ss->phase - 3) & GDITHER_SH_BUF_MASK] - * shaped_bs[3] - + ss->buffer[(ss->phase - 4) & GDITHER_SH_BUF_MASK] - * shaped_bs[4]; - - /* Roll buffer and store last error */ - ss->phase = (ss->phase + 1) & GDITHER_SH_BUF_MASK; - ss->buffer[ss->phase] = (float)lrintf(tmp) - ideal; - break; - } - - clamped = lrintf(tmp); - if (clamped > clamp_u) { - clamped = clamp_u; - } else if (clamped < clamp_l) { - clamped = clamp_l; - } - - switch (bit_depth) { - case GDither8bit: - o8[i] = (u_int8_t) (clamped * post_scale); - break; - case GDither16bit: - o16[i] = (int16_t) (clamped * post_scale); - break; - case GDither32bit: - o32[i] = (int32_t) (clamped * post_scale); - break; - } - } -} - -/* floating pint version of the inner loop function */ -inline static void gdither_innner_loop_fp(const GDitherType dt, - const uint32_t stride, const float bias, const float scale, - - const float post_scale, const int bit_depth, - const uint32_t channel, const uint32_t length, float *ts, - - GDitherShapedState *ss, float *x, void *y, const int clamp_u, - - const int clamp_l) -{ - uint32_t pos, i; - float *oflt = (float*) y; - double *odbl = (double*) y; - float tmp, r, ideal; - double clamped; - - i = channel; - for (pos = 0; pos < length; pos++, i += stride) { - tmp = x[i] * scale + bias; - - switch (dt) { - case GDitherNone: - break; - case GDitherRect: - tmp -= GDITHER_NOISE; - break; - case GDitherTri: - r = GDITHER_NOISE - 0.5f; - tmp -= r - ts[channel]; - ts[channel] = r; - break; - case GDitherShaped: - /* Save raw value for error calculations */ - ideal = tmp; - - /* Run FIR and add white noise */ - ss->buffer[ss->phase] = GDITHER_NOISE * 0.5f; - tmp += ss->buffer[ss->phase] * shaped_bs[0] - + ss->buffer[(ss->phase - 1) & GDITHER_SH_BUF_MASK] - * shaped_bs[1] - + ss->buffer[(ss->phase - 2) & GDITHER_SH_BUF_MASK] - * shaped_bs[2] - + ss->buffer[(ss->phase - 3) & GDITHER_SH_BUF_MASK] - * shaped_bs[3] - + ss->buffer[(ss->phase - 4) & GDITHER_SH_BUF_MASK] - * shaped_bs[4]; - - /* Roll buffer and store last error */ - ss->phase = (ss->phase + 1) & GDITHER_SH_BUF_MASK; - ss->buffer[ss->phase] = (float)lrintf(tmp) - ideal; - break; - } - - clamped = rintf(tmp); - if (clamped > clamp_u) { - clamped = clamp_u; - } else if (clamped < clamp_l) { - clamped = clamp_l; - } - - switch (bit_depth) { - case GDitherFloat: - oflt[i] = (float) (clamped * post_scale); - break; - case GDitherDouble: - odbl[i] = (double) (clamped * post_scale); - break; - } - } -} - -#define GDITHER_CONV_BLOCK 512 - -void gdither_run(GDither s, uint32_t channel, uint32_t length, - double *x, void *y) -{ - float conv[GDITHER_CONV_BLOCK]; - uint32_t i, pos; - char *ycast = (char *)y; - - int step; - - switch (s->bit_depth) { - case GDither8bit: - step = 1; - break; - case GDither16bit: - step = 2; - break; - case GDither32bit: - case GDitherFloat: - step = 4; - break; - case GDitherDouble: - step = 8; - break; - default: - step = 0; - break; - } - - pos = 0; - while (pos < length) { - for (i=0; (i + pos) < length && i < GDITHER_CONV_BLOCK; i++) { - conv[i] = x[pos + i]; - } - gdither_runf(s, channel, i, conv, ycast + s->channels * step); - pos += i; - } -} - -void gdither_runf(GDither s, uint32_t channel, uint32_t length, - float *x, void *y) -{ - uint32_t pos, i; - float tmp; - int64_t clamped; - GDitherShapedState *ss = NULL; - - if (!s || channel >= s->channels) { - return; - } - - if (s->shaped_state) { - ss = s->shaped_state + channel; - } - - if (s->type == GDitherNone && s->bit_depth == 23) { - int32_t *o32 = (int32_t*) y; - - for (pos = 0; pos < length; pos++) { - i = channel + (pos * s->channels); - tmp = x[i] * 8388608.0f; - - clamped = lrintf(tmp); - if (clamped > 8388607) { - clamped = 8388607; - } else if (clamped < -8388608) { - clamped = -8388608; - } - - o32[i] = (int32_t) (clamped * 256); - } - - return; - } - - /* some common case handling code - looks a bit wierd, but it allows - * the compiler to optimise out the branches in the inner loop */ - if (s->bit_depth == 8 && s->dither_depth == 8) { - switch (s->type) { - case GDitherNone: - gdither_innner_loop(GDitherNone, s->channels, 128.0f, SCALE_U8, - 1, 8, channel, length, NULL, NULL, x, y, - MAX_U8, MIN_U8); - break; - case GDitherRect: - gdither_innner_loop(GDitherRect, s->channels, 128.0f, SCALE_U8, - 1, 8, channel, length, NULL, NULL, x, y, - MAX_U8, MIN_U8); - break; - case GDitherTri: - gdither_innner_loop(GDitherTri, s->channels, 128.0f, SCALE_U8, - 1, 8, channel, length, s->tri_state, - NULL, x, y, MAX_U8, MIN_U8); - break; - case GDitherShaped: - gdither_innner_loop(GDitherShaped, s->channels, 128.0f, SCALE_U8, - 1, 8, channel, length, NULL, - ss, x, y, MAX_U8, MIN_U8); - break; - } - } else if (s->bit_depth == 16 && s->dither_depth == 16) { - switch (s->type) { - case GDitherNone: - gdither_innner_loop(GDitherNone, s->channels, 0.0f, SCALE_S16, - 1, 16, channel, length, NULL, NULL, x, y, - MAX_S16, MIN_S16); - break; - case GDitherRect: - gdither_innner_loop(GDitherRect, s->channels, 0.0f, SCALE_S16, - 1, 16, channel, length, NULL, NULL, x, y, - MAX_S16, MIN_S16); - break; - case GDitherTri: - gdither_innner_loop(GDitherTri, s->channels, 0.0f, SCALE_S16, - 1, 16, channel, length, s->tri_state, - NULL, x, y, MAX_S16, MIN_S16); - break; - case GDitherShaped: - gdither_innner_loop(GDitherShaped, s->channels, 0.0f, - SCALE_S16, 1, 16, channel, length, NULL, - ss, x, y, MAX_S16, MIN_S16); - break; - } - } else if (s->bit_depth == 32 && s->dither_depth == 24) { - switch (s->type) { - case GDitherNone: - gdither_innner_loop(GDitherNone, s->channels, 0.0f, SCALE_S24, - 256, 32, channel, length, NULL, NULL, x, - y, MAX_S24, MIN_S24); - break; - case GDitherRect: - gdither_innner_loop(GDitherRect, s->channels, 0.0f, SCALE_S24, - 256, 32, channel, length, NULL, NULL, x, - y, MAX_S24, MIN_S24); - break; - case GDitherTri: - gdither_innner_loop(GDitherTri, s->channels, 0.0f, SCALE_S24, - 256, 32, channel, length, s->tri_state, - NULL, x, y, MAX_S24, MIN_S24); - break; - case GDitherShaped: - gdither_innner_loop(GDitherShaped, s->channels, 0.0f, SCALE_S24, - 256, 32, channel, length, - NULL, ss, x, y, MAX_S24, MIN_S24); - break; - } - } else if (s->bit_depth == GDitherFloat || s->bit_depth == GDitherDouble) { - gdither_innner_loop_fp(s->type, s->channels, s->bias, s->scale, - s->post_scale_fp, s->bit_depth, channel, length, - s->tri_state, ss, x, y, s->clamp_u, s->clamp_l); - } else { - /* no special case handling, just process it from the struct */ - - gdither_innner_loop(s->type, s->channels, s->bias, s->scale, - s->post_scale, s->bit_depth, channel, - length, s->tri_state, ss, x, y, s->clamp_u, - s->clamp_l); - } -} - -/* vi:set ts=8 sts=4 sw=4: */ diff --git a/libs/ardour/session_export.cc b/libs/ardour/session_export.cc index 840c752bad..86a6250ffd 100644 --- a/libs/ardour/session_export.cc +++ b/libs/ardour/session_export.cc @@ -25,10 +25,8 @@ #include "ardour/audioengine.h" #include "ardour/butler.h" #include "ardour/export_failed.h" -#include "ardour/export_file_io.h" #include "ardour/export_handler.h" #include "ardour/export_status.h" -#include "ardour/export_utilities.h" #include "ardour/route.h" #include "ardour/session.h" @@ -183,7 +181,8 @@ Session::process_export (nframes_t nframes) ProcessExport (nframes); - } catch (ExportFailed e) { + } catch (std::exception & e) { + std::cout << e.what() << std::endl; export_status->abort (true); } } diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 97ad05cb68..2e2211b611 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -79,26 +79,23 @@ libardour_sources = [ 'event_type_map.cc', 'export_channel.cc', 'export_channel_configuration.cc', - 'export_file_io.cc', 'export_filename.cc', 'export_format_base.cc', 'export_format_manager.cc', 'export_format_specification.cc', 'export_formats.cc', + 'export_graph_builder.cc', 'export_handler.cc', 'export_preset.cc', - 'export_processor.cc', 'export_profile_manager.cc', 'export_status.cc', 'export_timespan.cc', - 'export_utilities.cc', 'file_source.cc', 'filename_extensions.cc', 'filesystem_paths.cc', 'filter.cc', 'find_session.cc', 'gain.cc', - 'gdither.cc', 'globals.cc', 'import.cc', 'internal_return.cc', @@ -268,7 +265,7 @@ def build(bld): obj.name = 'libardour' obj.target = 'ardour' obj.uselib = 'GLIBMM GTHREAD AUBIO SIGCPP XML UUID JACK SNDFILE SAMPLERATE LRDF OSX COREAUDIO' - obj.uselib_local = 'libpbd libmidipp libevoral libvamphost libvampplugin libtaglib librubberband' + obj.uselib_local = 'libpbd libmidipp libevoral libvamphost libvampplugin libtaglib librubberband libaudiographer' obj.vnum = LIBARDOUR_LIB_VERSION obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') obj.cxxflags = ['-DPACKAGE="libardour3"'] |