diff options
author | Taybin Rutkin <taybin@taybin.com> | 2005-09-24 19:13:41 +0000 |
---|---|---|
committer | Taybin Rutkin <taybin@taybin.com> | 2005-09-24 19:13:41 +0000 |
commit | 8af0757b61990767f2a85e68f535a5af9976fd79 (patch) | |
tree | f9e06fe12cac866d658a2e7074a61aa74d12f68f /libs/ardour/session_export.cc | |
parent | f9546e5c76afa101e9dbe8a057e72463b03430e5 (diff) |
libardour added.
git-svn-id: svn://localhost/trunk/ardour2@17 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour/session_export.cc')
-rw-r--r-- | libs/ardour/session_export.cc | 640 |
1 files changed, 640 insertions, 0 deletions
diff --git a/libs/ardour/session_export.cc b/libs/ardour/session_export.cc new file mode 100644 index 0000000000..a17dde6979 --- /dev/null +++ b/libs/ardour/session_export.cc @@ -0,0 +1,640 @@ +/* + Copyright (C) 1999-2002 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. + + $Id$ +*/ + +/* 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 <sigc++/bind.h> + +#include <pbd/error.h> +#include <pbd/lockmonitor.h> + +#include <ardour/gdither.h> +#include <ardour/timestamps.h> +#include <ardour/ardour.h> +#include <ardour/session.h> +#include <ardour/export.h> +#include <ardour/sndfile_helpers.h> +#include <ardour/port.h> +#include <ardour/audioengine.h> +#include <ardour/diskstream.h> +#include <ardour/panner.h> + +#include "i18n.h" + +using namespace std; +using namespace ARDOUR; +//using namespace sigc; + +static int +convert_spec_to_info (AudioExportSpecification& spec, SF_INFO& sfinfo) +{ + if (spec.path.length() == 0) { + error << _("Export: no output file specified") << endmsg; + return -1; + } + + /* XXX add checks that the directory path exists, and also + check if we are overwriting an existing file... + */ + + sfinfo.format = spec.format; + sfinfo.samplerate = spec.sample_rate; + sfinfo.frames = spec.end_frame - spec.start_frame + 1; + sfinfo.channels = min (spec.channels, 2U); + + return 0; +} + +AudioExportSpecification::AudioExportSpecification () +{ + init (); +} + +AudioExportSpecification::~AudioExportSpecification () +{ + clear (); +} + +void +AudioExportSpecification::init () +{ + src_state = 0; + pos = 0; + total_frames = 0; + out = 0; + channels = 0; + running = false; + stop = false; + progress = 0.0; + status = 0; + dither = 0; + start_frame = 0; + end_frame = 0; + dataF = 0; + dataF2 = 0; + leftoverF = 0; + max_leftover_frames = 0; + leftover_frames = 0; + output_data = 0; + out_samples_max = 0; + data_width = 0; + do_freewheel = false; +} + +void +AudioExportSpecification::clear () +{ + if (out) { + sf_close (out); + out = 0; + } + + if (src_state) { + src_delete (src_state); + src_state = 0; + } + + if (dither) { + gdither_free (dither); + dither = 0; + } + + if (output_data) { + free (output_data); + output_data = 0; + } + if (dataF) { + delete [] dataF; + dataF = 0; + } + if (dataF2) { + delete [] dataF2; + dataF2 = 0; + } + if (leftoverF) { + delete [] leftoverF; + leftoverF = 0; + } + + freewheel_connection.disconnect (); + + init (); +} + +int +AudioExportSpecification::prepare (jack_nframes_t blocksize, jack_nframes_t frate) +{ + char errbuf[256]; + GDitherSize dither_size; + + frame_rate = frate; + + if (channels == 0) { + error << _("illegal frame range in export specification") << endmsg; + return -1; + } + + if (start_frame >= end_frame) { + error << _("illegal frame range in export specification") << endmsg; + return -1; + } + + if ((data_width = sndfile_data_width(format)) == 0) { + error << _("Bad data width size. Report me!") << endmsg; + return -1; + } + + switch (data_width) { + case 8: + dither_size = GDither8bit; + break; + + case 16: + dither_size = GDither16bit; + break; + + case 24: + dither_size = GDither32bit; + break; + + default: + dither_size = GDitherFloat; + break; + } + + if (convert_spec_to_info (*this, sfinfo)) { + return -1; + } + + /* XXX make sure we have enough disk space for the output */ + + if ((out = sf_open (path.c_str(), SFM_WRITE, &sfinfo)) == 0) { + sf_error_str (0, errbuf, sizeof (errbuf) - 1); + error << compose(_("Export: cannot open output file \"%1\" (%2)"), path, errbuf) << endmsg; + return -1; + } + + dataF = new float[blocksize * channels]; + + if (sample_rate != frame_rate) { + int err; + + if ((src_state = src_new (src_quality, channels, &err)) == 0) { + error << compose (_("cannot initialize sample rate conversion: %1"), src_strerror (err)) << endmsg; + return -1; + } + + src_data.src_ratio = sample_rate / (double) frame_rate; + out_samples_max = (jack_nframes_t) ceil (blocksize * src_data.src_ratio * channels); + dataF2 = new float[out_samples_max]; + + max_leftover_frames = 4 * blocksize; + leftoverF = new float[max_leftover_frames * channels]; + leftover_frames = 0; + + } else { + out_samples_max = blocksize * channels; + } + + dither = gdither_new (dither_type, channels, dither_size, data_width); + + /* allocate buffers where dithering and output will occur */ + + switch (data_width) { + case 8: + sample_bytes = 1; + break; + + case 16: + sample_bytes = 2; + break; + + case 24: + case 32: + sample_bytes = 4; + break; + + default: + sample_bytes = 0; // float format + break; + } + + if (sample_bytes) { + output_data = (void*) malloc (sample_bytes * out_samples_max); + } + + return 0; +} + +int +AudioExportSpecification::process (jack_nframes_t nframes) +{ + float* float_buffer = 0; + uint32_t chn; + uint32_t x; + uint32_t i; + sf_count_t written; + char errbuf[256]; + jack_nframes_t to_write = 0; + int cnt = 0; + + do { + + /* now do sample rate conversion */ + + if (sample_rate != frame_rate) { + + int err; + + src_data.output_frames = out_samples_max / channels; + src_data.end_of_input = ((pos + nframes) >= end_frame); + src_data.data_out = dataF2; + + if (leftover_frames > 0) { + + /* input data will be in leftoverF rather than dataF */ + + src_data.data_in = leftoverF; + + if (cnt == 0) { + + /* first time, append new data from dataF into the leftoverF buffer */ + + memcpy (leftoverF + (leftover_frames * channels), dataF, nframes * channels * sizeof(float)); + src_data.input_frames = nframes + leftover_frames; + } else { + + /* otherwise, just use whatever is still left in leftoverF; 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 = dataF; + src_data.input_frames = nframes; + + } + + ++cnt; + + if ((err = src_process (src_state, &src_data)) != 0) { + error << compose (_("an error occured during sample rate conversion: %1"), + src_strerror (err)) + << endmsg; + return -1; + } + + to_write = 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 (leftoverF, (char *) (src_data.data_in + (src_data.input_frames_used * channels)), + leftover_frames * channels * sizeof(float)); + } + + float_buffer = dataF2; + + } else { + + /* no SRC, keep it simple */ + + to_write = nframes; + leftover_frames = 0; + float_buffer = dataF; + } + + if (output_data) { + memset (output_data, 0, sample_bytes * to_write * channels); + } + + switch (data_width) { + case 8: + case 16: + case 24: + for (chn = 0; chn < channels; ++chn) { + gdither_runf (dither, chn, to_write, float_buffer, output_data); + } + break; + + case 32: + for (chn = 0; chn < channels; ++chn) { + + int *ob = (int *) output_data; + const double int_max = (float) INT_MAX; + const double int_min = (float) INT_MIN; + + for (x = 0; x < to_write; ++x) { + i = chn + (x * channels); + + if (float_buffer[i] > 1.0f) { + ob[i] = INT_MAX; + } else if (float_buffer[i] < -1.0f) { + ob[i] = INT_MIN; + } else { + if (float_buffer[i] >= 0.0f) { + ob[i] = lrintf (int_max * float_buffer[i]); + } else { + ob[i] = - lrintf (int_min * float_buffer[i]); + } + } + } + } + break; + + default: + for (x = 0; x < to_write * channels; ++x) { + if (float_buffer[x] > 1.0f) { + float_buffer[x] = 1.0f; + } else if (float_buffer[x] < -1.0f) { + float_buffer[x] = -1.0f; + } + } + break; + } + + /* and export to disk */ + + switch (data_width) { + case 8: + /* XXXX no way to deliver 8 bit audio to libsndfile */ + written = to_write; + break; + + case 16: + written = sf_writef_short (out, (short*) output_data, to_write); + break; + + case 24: + case 32: + written = sf_writef_int (out, (int*) output_data, to_write); + break; + + default: + written = sf_writef_float (out, float_buffer, to_write); + break; + } + + if ((jack_nframes_t) written != to_write) { + sf_error_str (out, errbuf, sizeof (errbuf) - 1); + error << compose(_("Export: could not write data to output file (%1)"), errbuf) << endmsg; + return -1; + } + + + } while (leftover_frames >= nframes); + + return 0; +} + +int +Session::start_audio_export (AudioExportSpecification& spec) +{ + int ret; + + if (spec.prepare (current_block_size, frame_rate())) { + return -1; + } + + spec.pos = spec.start_frame; + spec.end_frame = spec.end_frame; + spec.total_frames = spec.end_frame - spec.start_frame; + + spec.freewheel_connection = _engine.Freewheel.connect (sigc::bind (mem_fun (*this, &Session::process_export), &spec)); + + if ((ret = _engine.freewheel (true)) == 0) { + spec.running = true; + spec.do_freewheel = false; + } + + return ret; +} + +int +Session::stop_audio_export (AudioExportSpecification& spec) +{ + /* can't use stop_transport() here because we need + an immediate halt and don't require all the declick + stuff that stop_transport() implements. + */ + + realtime_stop (true); + schedule_butler_transport_work (); + + /* restart slaving */ + + if (post_export_slave != None) { + set_slave_source (post_export_slave, post_export_position); + } else { + locate (post_export_position, false, false, false); + } + + spec.clear (); + _exporting = false; + + spec.running = false; + + return 0; +} + +int +Session::prepare_to_export (AudioExportSpecification& spec) +{ + int ret = -1; + + wait_till_butler_finished (); + + /* take everyone out of awrite to avoid disasters */ + + { + LockMonitor lm (route_lock, __LINE__, __FILE__); + for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) { + (*i)->protect_automation (); + } + } + + /* get everyone to the right position */ + + { + LockMonitor lm (diskstream_lock, __LINE__, __FILE__); + for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) { + if ((*i)-> seek (spec.start_frame, true)) { + error << compose (_("%1: cannot seek to %2 for export"), + (*i)->name(), spec.start_frame) + << endmsg; + goto out; + } + } + } + + /* make sure we are actually rolling */ + + if (get_record_enabled()) { + disable_record (); + } + + _exporting = true; + + /* no slaving */ + + post_export_slave = _slave_type; + post_export_position = _transport_frame; + + set_slave_source (None, 0); + + /* get transport ready */ + + set_transport_speed (1.0, false); + butler_transport_work (); + atomic_set (&butler_should_do_transport_work, 0); + post_transport (); + + /* we are ready to go ... */ + + ret = 0; + + out: + return ret; +} + +int +Session::process_export (jack_nframes_t nframes, AudioExportSpecification* spec) +{ + uint32_t chn; + uint32_t x; + int ret = -1; + jack_nframes_t this_nframes; + + /* This is not required to be RT-safe because we are running while freewheeling */ + + if (spec->do_freewheel == false) { + + /* first time in export function: get set up */ + + if (prepare_to_export (*spec)) { + spec->running = false; + spec->status = -1; + return -1; + } + + spec->do_freewheel = true; + } + + if (!_exporting) { + /* finished, but still freewheeling */ + process_without_events (nframes); + return 0; + } + + if (!spec->running || spec->stop || (this_nframes = min ((spec->end_frame - spec->pos), nframes)) == 0) { + process_without_events (nframes); + return stop_audio_export (*spec); + } + + /* make sure we've caught up with disk i/o, since + we're running faster than realtime c/o JACK. + */ + + wait_till_butler_finished (); + + /* do the usual stuff */ + + process_without_events (nframes); + + /* and now export the results */ + + nframes = this_nframes; + + memset (spec->dataF, 0, sizeof (spec->dataF[0]) * nframes * spec->channels); + + /* foreach output channel ... */ + + for (chn = 0; chn < spec->channels; ++chn) { + + AudioExportPortMap::iterator mi = spec->port_map.find (chn); + + if (mi == spec->port_map.end()) { + /* no ports exported to this channel */ + continue; + } + + vector<PortChannelPair>& mapped_ports ((*mi).second); + + for (vector<PortChannelPair>::iterator t = mapped_ports.begin(); t != mapped_ports.end(); ++t) { + + /* OK, this port's output is supposed to appear on this channel + */ + + Port* port = (*t).first; + Sample* port_buffer = port->get_buffer (nframes); + + /* now interleave the data from the channel into the float buffer */ + + for (x = 0; x < nframes; ++x) { + spec->dataF[chn+(x*spec->channels)] += (float) port_buffer[x]; + } + } + } + + if (spec->process (nframes)) { + goto out; + } + + spec->pos += nframes; + spec->progress = 1.0 - (((float) spec->end_frame - spec->pos) / spec->total_frames); + + /* and we're good to go */ + + ret = 0; + + out: + if (ret) { + sf_close (spec->out); + spec->out = 0; + unlink (spec->path.c_str()); + spec->running = false; + spec->status = ret; + _exporting = false; + } + + return ret; +} + |