/* 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 #include "ardour/export_file_io.h" #include "ardour/export_failed.h" #include "pbd/failed_constructor.h" #include "i18n.h" using namespace PBD; namespace ARDOUR { /* SndfileWriterBase */ SndfileWriterBase::SndfileWriterBase (int channels, nframes_t samplerate, int format, string const & path) : ExportFileWriter (path) { char errbuf[256]; sf_info.channels = channels; sf_info.samplerate = samplerate; sf_info.format = format; if (!sf_format_check (&sf_info)) { throw ExportFailed (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 SndfileWriter::SndfileWriter (int channels, nframes_t samplerate, int format, string const & path) : SndfileWriterBase (channels, samplerate, format, path) { // init write function init (); } template <> void SndfileWriter::init () { write_func = &sf_writef_float; } template <> void SndfileWriter::init () { write_func = &sf_writef_int; } template <> void SndfileWriter::init () { write_func = &sf_writef_short; } template nframes_t SndfileWriter::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::end_of_input) { sf_write_sync (sndfile); } return frames; } template class SndfileWriter; template class SndfileWriter; template class SndfileWriter; /* ExportTempFile */ ExportTempFile::ExportTempFile (uint32_t channels, nframes_t samplerate) : SndfileWriter (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 > ShortConverterPtr; typedef boost::shared_ptr > IntConverterPtr; typedef boost::shared_ptr > FloatConverterPtr; typedef boost::shared_ptr > ShortWriterPtr; typedef boost::shared_ptr > IntWriterPtr; typedef boost::shared_ptr > 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 (channels, format->dither_type(), data_width)); ShortWriterPtr sfw = ShortWriterPtr (new SndfileWriter (channels, format->sample_rate(), real_format, filename)); sfc->pipe_to (sfw); return std::make_pair (boost::static_pointer_cast (sfc), boost::static_pointer_cast (sfw)); } else if (data_width == 24 || data_width == 32) { IntConverterPtr sfc = IntConverterPtr (new SampleFormatConverter (channels, format->dither_type(), data_width)); IntWriterPtr sfw = IntWriterPtr (new SndfileWriter (channels, format->sample_rate(), real_format, filename)); sfc->pipe_to (sfw); return std::make_pair (boost::static_pointer_cast (sfc), boost::static_pointer_cast (sfw)); } FloatConverterPtr sfc = FloatConverterPtr (new SampleFormatConverter (channels, format->dither_type(), data_width)); FloatWriterPtr sfw = FloatWriterPtr (new SndfileWriter (channels, format->sample_rate(), real_format, filename)); sfc->pipe_to (sfw); return std::make_pair (boost::static_pointer_cast (sfc), boost::static_pointer_cast (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