diff options
Diffstat (limited to 'libs/ardour/export_processor.cc')
-rw-r--r-- | libs/ardour/export_processor.cc | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/libs/ardour/export_processor.cc b/libs/ardour/export_processor.cc new file mode 100644 index 0000000000..87f1772c90 --- /dev/null +++ b/libs/ardour/export_processor.cc @@ -0,0 +1,347 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <ardour/export_processor.h> + +#include <pbd/error.h> +#include <pbd/filesystem.h> + +#include <ardour/session.h> +#include <ardour/audiofile_tagger.h> +#include <ardour/broadcast_info.h> +#include <ardour/export_failed.h> +#include <ardour/export_filename.h> +#include <ardour/export_status.h> +#include <ardour/export_format_specification.h> +#include <ardour/sndfile_helpers.h> + +#include "i18n.h" + +using namespace PBD; + +namespace ARDOUR +{ + +sigc::signal<void, Glib::ustring> ExportProcessor::WritingFile; + +ExportProcessor::ExportProcessor (Session & session) : + session (session), + status (session.export_status), + blocksize (session.get_block_size()), + frame_rate (session.frame_rate()) +{ + reset (); +} + +ExportProcessor::~ExportProcessor () +{ + +} + +void +ExportProcessor::reset () +{ + file_sinks.clear(); + writer_list.clear(); + filename.reset(); + normalizer.reset(); + src.reset(); + peak_reader.reset(); + temp_file.reset(); +} + +int +ExportProcessor::prepare (FormatPtr format, FilenamePtr fname, uint32_t chans, bool split, nframes_t start) +{ + session.export_status.format++; + temp_file_length = 0; + + /* Reset just to be sure all references are dropped */ + + reset(); + + /* Get parameters needed later on */ + + channels = chans; + split_files = split; + filename = fname; + tag = format->tag(); + broadcast_info = format->has_broadcast_info(); + normalize = format->normalize(); + trim_beginning = format->trim_beginning(); + trim_end = format->trim_end(); + silence_beginning = format->silence_beginning(); + silence_end = format->silence_end(); + + /* SRC */ + + src.reset (new SampleRateConverter (channels, frame_rate, format->sample_rate(), format->src_quality())); + + /* Construct export pipe to temp file */ + + status.stage = export_PostProcess; + + if (normalize) { + /* Normalizing => we need a normalizer, peak reader and tempfile */ + + normalizer.reset (new Normalizer (channels, format->normalize_target())); + + peak_reader.reset (new PeakReader (channels)); + temp_file.reset (new ExportTempFile (channels, format->sample_rate())); + + src->pipe_to (peak_reader); + peak_reader->pipe_to (temp_file); + + } else if (trim_beginning || trim_end) { + /* Not normalizing, but silence will be trimmed => need for a tempfile */ + + temp_file.reset (new ExportTempFile (channels, format->sample_rate())); + src->pipe_to (temp_file); + + } else { + /* Due to complexity and time running out, a tempfile will be created for this also... */ + + temp_file.reset (new ExportTempFile (channels, format->sample_rate())); + src->pipe_to (temp_file); + } + + /* File writer(s) */ + + FloatSinkPtr (ExportProcessor::*prep_function) (FormatPtr, uint32_t, ustring const &); + + switch (format->type()) { + case ExportFormatBase::T_Sndfile: + prep_function = &ExportProcessor::prepare_sndfile_writer; + break; + + default: + throw ExportFailed (_("Export failed due to a programming error"), "Invalid format given for ExportProcessor::prepare!"); + break; + + } + + /* Ensure directory exists */ + + sys::path folder (filename->get_folder()); + if (!sys::exists (folder)) { + if (!sys::create_directory (folder)) { + throw ExportFailed ("Export could not create the directory you requested for", "sys::create_directory failed for export dir"); + } + } + + /* prep file sinks */ + + if (split) { + filename->include_channel = true; + for (uint32_t chn = 1; chn <= channels; ++chn) { + filename->set_channel (chn); + file_sinks.push_back ((this->*prep_function) (format, 1, filename->get_path (format))); + WritingFile (filename->get_path (format)); + } + + } else { + file_sinks.push_back ((this->*prep_function) (format, channels, filename->get_path (format))); + WritingFile (filename->get_path (format)); + } + + /* Set position info */ + + nframes_t start_position = ((double) format->sample_rate() / frame_rate) * start + 0.5; + + for (FileWriterList::iterator it = writer_list.begin(); it != writer_list.end(); ++it) { + (*it)->set_position (start_position); + } + + /* set broadcast info if necessary */ + + if (broadcast_info) { + for (FileWriterList::iterator it = writer_list.begin(); it != writer_list.end(); ++it) { + + BroadcastInfo bci; + bci.set_from_session (session, (*it)->position()); + + boost::shared_ptr<SndfileWriterBase> sndfile_ptr; + if ((sndfile_ptr = boost::dynamic_pointer_cast<SndfileWriterBase> (*it))) { + if (!bci.write_to_file (sndfile_ptr->get_sndfile())) { + std::cerr << bci.get_error() << std::endl; + } + } else { + if (!bci.write_to_file ((*it)->filename())) { + std::cerr << bci.get_error() << std::endl; + } + } + } + } + + return 0; +} + +nframes_t +ExportProcessor::process (float * data, nframes_t frames) +{ + nframes_t frames_written = src->write (data, frames); + temp_file_length += frames_written; + return frames_written; +} + +void +ExportProcessor::prepare_post_processors () +{ + /* Set end of input and do last write */ + float dummy; + src->set_end_of_input (); + src->write (&dummy, 0); + + /* Trim and add silence */ + + temp_file->trim_beginning (trim_beginning); + temp_file->trim_end (trim_end); + + temp_file->set_silence_beginning (silence_beginning); + temp_file->set_silence_end (silence_end); + + /* Set up normalizer */ + + if (normalize) { + normalizer->set_peak (peak_reader->get_peak ()); + } +} + +void +ExportProcessor::write_files () +{ + /* Write to disk */ + + status.stage = export_Write; + temp_file_position = 0; + + uint32_t buffer_size = 4096; // TODO adjust buffer size? + float * buf = new float[channels * buffer_size]; + int frames_read; + + FloatSinkPtr disk_sink; + + if (normalize) { + disk_sink = boost::dynamic_pointer_cast<FloatSink> (normalizer); + normalizer->pipe_to (file_sinks[0]); + } else { + disk_sink = file_sinks[0]; + } + + if (split_files) { + + /* Get buffers for each channel separately */ + + std::vector<float *> chan_bufs; + + for (uint32_t i = 0; i < channels; ++i) { + chan_bufs.push_back(new float[buffer_size]); + } + + /* de-interleave and write files */ + + while ((frames_read = temp_file->read (buf, buffer_size)) > 0) { + for (uint32_t channel = 0; channel < channels; ++channel) { + for (uint32_t i = 0; i < buffer_size; ++i) { + chan_bufs[channel][i] = buf[channel + (channels * i)]; + } + if (normalize) { + normalizer->pipe_to (file_sinks[channel]); + } else { + disk_sink = file_sinks[channel]; + } + disk_sink->write (chan_bufs[channel], frames_read); + } + + if (status.aborted()) { break; } + temp_file_position += frames_read; + status.progress = (float) temp_file_position / temp_file_length; + } + + /* Clean up */ + + for (std::vector<float *>::iterator it = chan_bufs.begin(); it != chan_bufs.end(); ++it) { + delete[] *it; + } + + } else { + while ((frames_read = temp_file->read (buf, buffer_size)) > 0) { + disk_sink->write (buf, frames_read); + + if (status.aborted()) { break; } + temp_file_position += frames_read; + status.progress = (float) temp_file_position / temp_file_length; + } + } + + delete [] buf; + + /* Tag files if necessary and send exported signal */ + + + for (FileWriterList::iterator it = writer_list.begin(); it != writer_list.end(); ++it) { + if (tag) { + AudiofileTagger::tag_file ((*it)->filename(), session.metadata()); + } + session.Exported ((*it)->filename(), session.name()); + } +} + +ExportProcessor::FloatSinkPtr +ExportProcessor::prepare_sndfile_writer (FormatPtr format, uint32_t channels, ustring const & filename) +{ + int real_format = format->format_id() | format->sample_format() | format->endianness(); + + uint32_t data_width = sndfile_data_width (real_format); + + if (data_width == 8 || data_width == 16) { + + ShortConverterPtr sfc = ShortConverterPtr (new SampleFormatConverter<short> (channels, format->dither_type(), data_width)); + ShortWriterPtr sfw = ShortWriterPtr (new SndfileWriter<short> (channels, format->sample_rate(), real_format, filename)); + + writer_list.push_back (boost::static_pointer_cast<ExportFileWriter> (sfw)); + + sfc->pipe_to (sfw); + return boost::static_pointer_cast<FloatSink> (sfc); + + } else if (data_width == 24 || data_width == 32) { + + IntConverterPtr sfc = IntConverterPtr (new SampleFormatConverter<int> (channels, format->dither_type(), data_width)); + IntWriterPtr sfw = IntWriterPtr (new SndfileWriter<int> (channels, format->sample_rate(), real_format, filename)); + + writer_list.push_back (boost::static_pointer_cast<ExportFileWriter> (sfw)); + + sfc->pipe_to (sfw); + return boost::static_pointer_cast<FloatSink> (sfc); + + } else { + + FloatConverterPtr sfc = FloatConverterPtr (new SampleFormatConverter<float> (channels, format->dither_type(), data_width)); + FloatWriterPtr sfw = FloatWriterPtr (new SndfileWriter<float> (channels, format->sample_rate(), real_format, filename)); + + writer_list.push_back (boost::static_pointer_cast<ExportFileWriter> (sfw)); + + sfc->pipe_to (sfw); + return boost::static_pointer_cast<FloatSink> (sfc); + } + +} + +}; // namespace ARDOUR |