summaryrefslogtreecommitdiff
path: root/libs/ardour/export_file_io.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libs/ardour/export_file_io.cc')
-rw-r--r--libs/ardour/export_file_io.cc366
1 files changed, 366 insertions, 0 deletions
diff --git a/libs/ardour/export_file_io.cc b/libs/ardour/export_file_io.cc
new file mode 100644
index 0000000000..ed60713903
--- /dev/null
+++ b/libs/ardour/export_file_io.cc
@@ -0,0 +1,366 @@
+/*
+ Copyright (C) 2008 Paul Davis
+ Author: Sakari Bergen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <ardour/export_file_io.h>
+#include <ardour/export_failed.h>
+#include <pbd/failed_constructor.h>
+
+#include "i18n.h"
+
+using namespace PBD;
+
+namespace ARDOUR
+{
+
+/* SndfileWriterBase */
+
+SndfileWriterBase::SndfileWriterBase (int channels, nframes_t samplerate, int format, string const & path) :
+ ExportFileWriter (path)
+{
+ char errbuf[256];
+
+ sf_info.channels = channels;
+ sf_info.samplerate = samplerate;
+ sf_info.format = format;
+
+ if (!sf_format_check (&sf_info)) {
+ throw ExportFailed (_("Export failed due to a programming error"), "Invalid format given for SndfileWriter!");
+ }
+
+ if (path.length() == 0) {
+ throw ExportFailed (_("Export failed due to a programming error"), "No output file specified for SndFileWriter");
+ }
+
+ /* TODO add checks that the directory path exists, and also
+ check if we are overwriting an existing file...
+ */
+
+ // Open file TODO make sure we have enough disk space for the output
+ if (path.compare ("temp")) {
+ if ((sndfile = sf_open (path.c_str(), SFM_WRITE, &sf_info)) == 0) {
+ sf_error_str (0, errbuf, sizeof (errbuf) - 1);
+ throw ExportFailed (string_compose(_("Export: cannot open output file \"%1\""), path),
+ string_compose(_("Export: cannot open output file \"%1\" for SndFileWriter (%2)"), path, errbuf));
+ }
+ } else {
+ FILE * file;
+ if (!(file = tmpfile ())) {
+ throw ExportFailed (_("Export failed due to a programming error"), "Cannot open tempfile");
+ }
+ sndfile = sf_open_fd (fileno(file), SFM_RDWR, &sf_info, true);
+ }
+}
+
+SndfileWriterBase::~SndfileWriterBase ()
+{
+ sf_close (sndfile);
+}
+
+/* SndfileWriter */
+
+template <typename T>
+SndfileWriter<T>::SndfileWriter (int channels, nframes_t samplerate, int format, string const & path) :
+ SndfileWriterBase (channels, samplerate, format, path)
+{
+ // init write function
+ init ();
+}
+
+template <>
+void
+SndfileWriter<float>::init ()
+{
+ write_func = &sf_writef_float;
+}
+
+template <>
+void
+SndfileWriter<int>::init ()
+{
+ write_func = &sf_writef_int;
+}
+
+template <>
+void
+SndfileWriter<short>::init ()
+{
+ write_func = &sf_writef_short;
+}
+
+template <typename T>
+nframes_t
+SndfileWriter<T>::write (T * data, nframes_t frames)
+{
+ char errbuf[256];
+ nframes_t written = (*write_func) (sndfile, data, frames);
+ if (written != frames) {
+ sf_error_str (sndfile, errbuf, sizeof (errbuf) - 1);
+ throw ExportFailed (_("Writing export file failed"), string_compose(_("Could not write data to output file (%1)"), errbuf));
+ }
+
+ if (GraphSink<T>::end_of_input) {
+ sf_write_sync (sndfile);
+ }
+
+ return frames;
+}
+
+template class SndfileWriter<short>;
+template class SndfileWriter<int>;
+template class SndfileWriter<float>;
+
+/* ExportTempFile */
+
+ExportTempFile::ExportTempFile (uint32_t channels, nframes_t samplerate) :
+ SndfileWriter<float> (channels, samplerate, SF_FORMAT_RAW | SF_FORMAT_FLOAT | SF_ENDIAN_FILE, "temp"),
+ channels (channels),
+ reading (false),
+ start (0),
+ end (0),
+ beginning_processed (false),
+ end_processed (false),
+ silence_beginning (0),
+ silence_end (0),
+ end_set (false)
+{
+}
+
+nframes_t
+ExportTempFile::read (float * data, nframes_t frames)
+{
+ nframes_t frames_read = 0;
+ nframes_t to_read = 0;
+ sf_count_t read_status = 0;
+
+ /* Initialize state at first read */
+
+ if (!reading) {
+ if (!end_set) {
+ end = get_length();
+ end_set = true;
+ }
+ locate_to (start);
+ reading = true;
+ }
+
+ /* Add silence to beginning */
+
+ if (silence_beginning > 0) {
+ if (silence_beginning >= frames) {
+ memset (data, 0, channels * frames * sizeof (float));
+ silence_beginning -= frames;
+ return frames;
+ }
+
+ memset (data, 0, channels * silence_beginning * sizeof (float));
+
+ frames_read += silence_beginning;
+ data += channels * silence_beginning;
+ silence_beginning = 0;
+ }
+
+ /* Read file, but don't read past end */
+
+ if (get_read_position() >= end) {
+ // File already read, do nothing!
+ } else {
+ if ((get_read_position() + (frames - frames_read)) > end) {
+ to_read = end - get_read_position();
+ } else {
+ to_read = frames - frames_read;
+ }
+
+ read_status = sf_readf_float (sndfile, data, to_read);
+
+ frames_read += to_read;
+ data += channels * to_read;
+ }
+
+ /* Check for errors */
+
+ if (read_status != to_read) {
+ throw ExportFailed (_("Reading export file failed"), _("Error reading temporary export file, export might not be complete!"));
+ }
+
+ /* Add silence at end */
+
+ if (silence_end > 0) {
+ to_read = frames - frames_read;
+ if (silence_end < to_read) {
+ to_read = silence_end;
+ }
+
+ memset (data, 0, channels * to_read * sizeof (float));
+ silence_end -= to_read;
+ frames_read += to_read;
+ }
+
+ return frames_read;
+}
+
+nframes_t
+ExportTempFile::trim_beginning (bool yn)
+{
+ if (!yn) {
+ start = 0;
+ return start;
+ }
+
+ if (!beginning_processed) {
+ process_beginning ();
+ }
+
+ start = silent_frames_beginning;
+ return start;
+
+}
+
+nframes_t
+ExportTempFile::trim_end (bool yn)
+{
+ end_set = true;
+
+ if (!yn) {
+ end = get_length();
+ return end;
+ }
+
+ if (!end_processed) {
+ process_end ();
+ }
+
+ end = silent_frames_end;
+ return end;
+}
+
+
+void
+ExportTempFile::process_beginning ()
+{
+ nframes_t frames = 1024;
+ nframes_t frames_read;
+ float * buf = new float[channels * frames];
+
+ nframes_t pos = 0;
+ locate_to (pos);
+
+ while ((frames_read = _read (buf, frames)) > 0) {
+ for (nframes_t i = 0; i < frames_read; i++) {
+ for (uint32_t chn = 0; chn < channels; ++chn) {
+ if (buf[chn + i * channels] != 0.0f) {
+ --pos;
+ goto out;
+ }
+ }
+ ++pos;
+ }
+ }
+
+ out:
+
+ silent_frames_beginning = pos;
+ beginning_processed = true;
+
+ delete [] buf;
+}
+
+void
+ExportTempFile::process_end ()
+{
+ nframes_t frames = 1024;
+ nframes_t frames_read;
+ float * buf = new float[channels * frames];
+
+ nframes_t pos = get_length() - 1;
+
+ while (pos > 0) {
+ if (pos > frames) {
+ locate_to (pos - frames);
+ frames_read = _read (buf, frames);
+ } else {
+ // Last time reading
+ locate_to (0);
+ frames_read = _read (buf, pos);
+ }
+
+ for (nframes_t i = frames_read; i > 0; --i) {
+ for (uint32_t chn = 0; chn < channels; ++chn) {
+ if (buf[chn + (i - 1) * channels] != 0.0f) {
+ goto out;
+ }
+ }
+ --pos;
+ }
+ }
+
+ out:
+
+ silent_frames_end = pos;
+ end_processed = true;
+
+ delete [] buf;
+}
+
+void
+ExportTempFile::set_silence_beginning (nframes_t frames)
+{
+ silence_beginning = frames;
+}
+
+void
+ExportTempFile::set_silence_end (nframes_t frames)
+{
+ silence_end = frames;
+}
+
+sf_count_t
+ExportTempFile::get_length ()
+{
+ sf_count_t pos = get_position();
+ sf_count_t len = sf_seek (sndfile, 0, SEEK_END);
+ locate_to (pos);
+ return len;
+}
+
+sf_count_t
+ExportTempFile::get_position ()
+{
+ return sf_seek (sndfile, 0, SEEK_CUR);
+}
+
+sf_count_t
+ExportTempFile::get_read_position ()
+{
+ return sf_seek (sndfile, 0, SEEK_CUR | SFM_READ);
+}
+
+sf_count_t
+ExportTempFile::locate_to (nframes_t frames)
+{
+ return sf_seek (sndfile, frames, SEEK_SET);
+}
+
+sf_count_t
+ExportTempFile::_read (float * data, nframes_t frames)
+{
+ return sf_readf_float (sndfile, data, frames);
+}
+
+};