diff options
author | Carl Hetherington <carl@carlh.net> | 2010-04-09 14:11:47 +0000 |
---|---|---|
committer | Carl Hetherington <carl@carlh.net> | 2010-04-09 14:11:47 +0000 |
commit | f4ac9430f3fc2c405334f3f02fe08a5782454396 (patch) | |
tree | 2858264db409fc1bfe778554f86100f01c1f0b3d /libs | |
parent | 77c09fc8248160ab60e2e229710d07934fe08894 (diff) |
Prevent clipping during the import of files from sources that have
amplitudes greater than 1 when data is being stored in files that
are clamped. e.g. when importing hot sources and resampling them
when the session file format is integer.
git-svn-id: svn://localhost/ardour2/branches/3.0@6879 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r-- | libs/ardour/ardour/audiosource.h | 3 | ||||
-rw-r--r-- | libs/ardour/ardour/importable_source.h | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/resampled_source.h | 8 | ||||
-rw-r--r-- | libs/ardour/ardour/silentfilesource.h | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/sndfileimportable.h | 1 | ||||
-rw-r--r-- | libs/ardour/ardour/sndfilesource.h | 2 | ||||
-rw-r--r-- | libs/ardour/import.cc | 50 | ||||
-rw-r--r-- | libs/ardour/resampled_source.cc | 60 | ||||
-rw-r--r-- | libs/ardour/sndfileimportable.cc | 8 | ||||
-rw-r--r-- | libs/ardour/sndfilesource.cc | 7 | ||||
-rw-r--r-- | libs/ardour/test/resampled_source.cc | 31 | ||||
-rw-r--r-- | libs/ardour/test/resampled_source.h | 12 | ||||
-rwxr-xr-x | libs/ardour/test/test.wav | bin | 0 -> 17720 bytes | |||
-rw-r--r-- | libs/ardour/wscript | 1 |
14 files changed, 158 insertions, 29 deletions
diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index e78fb049d7..9677986449 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -100,6 +100,9 @@ class AudioSource : virtual public Source, int prepare_for_peakfile_writes (); void done_with_peakfile_writes (bool done = true); + /** @return true if the each source sample s must be clamped to -1 < s < 1 */ + virtual bool clamped_at_unity () const = 0; + protected: static bool _build_missing_peakfiles; static bool _build_peakfiles; diff --git a/libs/ardour/ardour/importable_source.h b/libs/ardour/ardour/importable_source.h index 801e7888a9..7df346a782 100644 --- a/libs/ardour/ardour/importable_source.h +++ b/libs/ardour/ardour/importable_source.h @@ -37,6 +37,8 @@ public: virtual nframes_t samplerate() const = 0; virtual void seek (nframes_t pos) = 0; virtual nframes64_t natural_position() const = 0; + + virtual bool clamped_at_unity () const = 0; }; } diff --git a/libs/ardour/ardour/resampled_source.h b/libs/ardour/ardour/resampled_source.h index efa1458152..b61303b65c 100644 --- a/libs/ardour/ardour/resampled_source.h +++ b/libs/ardour/ardour/resampled_source.h @@ -39,14 +39,20 @@ class ResampledImportableSource : public ImportableSource uint32_t channels() const { return source->channels(); } nframes_t length() const { return source->length(); } nframes_t samplerate() const { return source->samplerate(); } - void seek (nframes_t pos) { source->seek (pos); } + void seek (nframes_t); nframes64_t natural_position() const { return source->natural_position(); } + bool clamped_at_unity () const { + /* resampling may generate inter-sample peaks with magnitude > 1 */ + return false; + } + static const uint32_t blocksize; private: boost::shared_ptr<ImportableSource> source; float* input; + int _src_type; SRC_STATE* src_state; SRC_DATA src_data; }; diff --git a/libs/ardour/ardour/silentfilesource.h b/libs/ardour/ardour/silentfilesource.h index 9b20f6f1b0..23128c4025 100644 --- a/libs/ardour/ardour/silentfilesource.h +++ b/libs/ardour/ardour/silentfilesource.h @@ -36,6 +36,8 @@ public: bool destructive() const { return false; } bool can_be_analysed() const { return false; } + bool clamped_at_unity() const { return false; } + protected: friend class SourceFactory; diff --git a/libs/ardour/ardour/sndfileimportable.h b/libs/ardour/ardour/sndfileimportable.h index 95c6d80ebb..6e308415c9 100644 --- a/libs/ardour/ardour/sndfileimportable.h +++ b/libs/ardour/ardour/sndfileimportable.h @@ -39,6 +39,7 @@ class SndFileImportableSource : public ImportableSource { nframes_t samplerate() const; void seek (nframes_t pos); nframes64_t natural_position() const; + bool clamped_at_unity () const; protected: SF_INFO sf_info; diff --git a/libs/ardour/ardour/sndfilesource.h b/libs/ardour/ardour/sndfilesource.h index 19dcb13536..96f39de2c8 100644 --- a/libs/ardour/ardour/sndfilesource.h +++ b/libs/ardour/ardour/sndfilesource.h @@ -57,6 +57,8 @@ class SndFileSource : public AudioFileSource { bool one_of_several_channels () const; + bool clamped_at_unity () const; + static void setup_standard_crossfades (Session const &, nframes_t sample_rate); static const Source::Flag default_writable_flags; diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index e05d162a56..61eb959829 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -277,8 +277,48 @@ write_audio_data_to_new_files (ImportableSource* source, ImportStatus& status, channel_data.push_back(boost::shared_array<Sample>(new Sample[nframes])); } - uint read_count = 0; + float gain = 1; + + boost::shared_ptr<AudioSource> s = boost::dynamic_pointer_cast<AudioSource> (newfiles[0]); + assert (s); + status.progress = 0.0f; + float progress_multiplier = 1; + float progress_base = 0; + + if (!source->clamped_at_unity() && s->clamped_at_unity()) { + + /* The source we are importing from can return sample values with a magnitude greater than 1, + and the file we are writing the imported data to cannot handle such values. Compute the gain + factor required to normalize the input sources to have a magnitude of less than 1. + */ + + float peak = 0; + uint read_count = 0; + + while (!status.cancel) { + nframes_t const nread = source->read (data.get(), nframes); + if (nread == 0) { + break; + } + + peak = compute_peak (data.get(), nread, peak); + + read_count += nread; + status.progress = 0.5 * read_count / (source->ratio() * source->length() * channels); + } + + if (peak >= 1) { + /* we are out of range: compute a gain to fix it */ + gain = (1 - FLT_EPSILON) / peak; + } + + source->seek (0); + progress_multiplier = 0.5; + progress_base = 0.5; + } + + uint read_count = 0; while (!status.cancel) { @@ -289,6 +329,12 @@ write_audio_data_to_new_files (ImportableSource* source, ImportStatus& status, if ((nread = source->read (data.get(), nframes)) == 0) { break; } + + if (gain != 1) { + /* here is the gain fix for out-of-range sample values that we computed earlier */ + apply_gain_to_buffer (data.get(), nread, gain); + } + nfread = nread / channels; /* de-interleave */ @@ -310,7 +356,7 @@ write_audio_data_to_new_files (ImportableSource* source, ImportStatus& status, } read_count += nread; - status.progress = read_count / (source->ratio () * source->length() * channels); + status.progress = progress_base + progress_multiplier * read_count / (source->ratio () * source->length() * channels); } } diff --git a/libs/ardour/resampled_source.cc b/libs/ardour/resampled_source.cc index 57811312f1..675a0e426d 100644 --- a/libs/ardour/resampled_source.cc +++ b/libs/ardour/resampled_source.cc @@ -30,48 +30,33 @@ const uint32_t ResampledImportableSource::blocksize = 16384U; ResampledImportableSource::ResampledImportableSource (boost::shared_ptr<ImportableSource> src, nframes_t rate, SrcQuality srcq) : source (src) + , src_state (0) { - int err; - - source->seek (0); - - /* Initialize the sample rate converter. */ - - int src_type = SRC_SINC_BEST_QUALITY; + _src_type = SRC_SINC_BEST_QUALITY; switch (srcq) { case SrcBest: - src_type = SRC_SINC_BEST_QUALITY; + _src_type = SRC_SINC_BEST_QUALITY; break; case SrcGood: - src_type = SRC_SINC_MEDIUM_QUALITY; + _src_type = SRC_SINC_MEDIUM_QUALITY; break; case SrcQuick: - src_type = SRC_SINC_FASTEST; + _src_type = SRC_SINC_FASTEST; break; case SrcFast: - src_type = SRC_ZERO_ORDER_HOLD; + _src_type = SRC_ZERO_ORDER_HOLD; break; case SrcFastest: - src_type = SRC_LINEAR; + _src_type = SRC_LINEAR; break; } - if ((src_state = src_new (src_type, source->channels(), &err)) == 0) { - error << string_compose(_("Import: src_new() failed : %1"), src_strerror (err)) << endmsg ; - throw failed_constructor (); - } - - src_data.end_of_input = 0 ; /* Set this later. */ - - /* Start with zero to force load in while loop. */ - - src_data.input_frames = 0 ; - src_data.data_in = input ; + input = new float[blocksize]; + seek (0); + src_data.src_ratio = ((float) rate) / source->samplerate(); - - input = new float[blocksize]; } ResampledImportableSource::~ResampledImportableSource () @@ -106,7 +91,7 @@ ResampledImportableSource::read (Sample* output, nframes_t nframes) if (!src_data.end_of_input) { src_data.output_frames = nframes / source->channels(); } else { - src_data.output_frames = src_data.input_frames; + src_data.output_frames = std::min ((nframes_t) src_data.input_frames, nframes / source->channels()); } if ((err = src_process (src_state, &src_data))) { @@ -126,3 +111,26 @@ ResampledImportableSource::read (Sample* output, nframes_t nframes) return src_data.output_frames_gen * source->channels(); } +void +ResampledImportableSource::seek (nframes_t pos) +{ + source->seek (pos); + + /* and reset things so that we start from scratch with the conversion */ + + if (src_state) { + src_delete (src_state); + } + + int err; + + if ((src_state = src_new (_src_type, source->channels(), &err)) == 0) { + error << string_compose(_("Import: src_new() failed : %1"), src_strerror (err)) << endmsg ; + throw failed_constructor (); + } + + src_data.input_frames = 0; + src_data.data_in = input; + src_data.end_of_input = 0; +} + diff --git a/libs/ardour/sndfileimportable.cc b/libs/ardour/sndfileimportable.cc index cc68f3ea01..758e8955c2 100644 --- a/libs/ardour/sndfileimportable.cc +++ b/libs/ardour/sndfileimportable.cc @@ -80,3 +80,11 @@ SndFileImportableSource::natural_position () const { return timecode; } + +bool +SndFileImportableSource::clamped_at_unity () const +{ + int const sub = sf_info.format & SF_FORMAT_SUBMASK; + /* XXX: this may not be the full list of formats that are unclamped */ + return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE); +} diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc index 05de692016..fa709cde24 100644 --- a/libs/ardour/sndfilesource.cc +++ b/libs/ardour/sndfilesource.cc @@ -833,3 +833,10 @@ SndFileSource::one_of_several_channels () const return _info.channels > 1; } +bool +SndFileSource::clamped_at_unity () const +{ + int const sub = _info.format & SF_FORMAT_SUBMASK; + /* XXX: this may not be the full list of formats that are unclamped */ + return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE); +} diff --git a/libs/ardour/test/resampled_source.cc b/libs/ardour/test/resampled_source.cc new file mode 100644 index 0000000000..02bd7ff1a8 --- /dev/null +++ b/libs/ardour/test/resampled_source.cc @@ -0,0 +1,31 @@ +#include "ardour/resampled_source.h" +#include "ardour/sndfileimportable.h" +#include "resampled_source.h" + +CPPUNIT_TEST_SUITE_REGISTRATION (ResampledSourceTest); + +using namespace ARDOUR; + +void +ResampledSourceTest::seekTest () +{ + boost::shared_ptr<SndFileImportableSource> s (new SndFileImportableSource ("../../libs/ardour/test/test.wav")); + ResampledImportableSource r (s, 48000, SrcBest); + + /* Make sure that seek (0) has the desired effect, ie that + given the same input you get the same output after seek (0) + as you got when the Source was newly created. + */ + + Sample A[64]; + r.read (A, 64); + + r.seek (0); + + Sample B[64]; + r.read (B, 64); + + for (int i = 0; i < 64; ++i) { + CPPUNIT_ASSERT (A[i] == B[i]); + } +} diff --git a/libs/ardour/test/resampled_source.h b/libs/ardour/test/resampled_source.h new file mode 100644 index 0000000000..c836968987 --- /dev/null +++ b/libs/ardour/test/resampled_source.h @@ -0,0 +1,12 @@ +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +class ResampledSourceTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE (ResampledSourceTest); + CPPUNIT_TEST (seekTest); + CPPUNIT_TEST_SUITE_END (); + +public: + void seekTest (); +}; diff --git a/libs/ardour/test/test.wav b/libs/ardour/test/test.wav Binary files differnew file mode 100755 index 0000000000..369531cc91 --- /dev/null +++ b/libs/ardour/test/test.wav diff --git a/libs/ardour/wscript b/libs/ardour/wscript index e49c1b1284..ce9d41d795 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -330,6 +330,7 @@ def build(bld): test/bbt_test.cpp test/interpolation_test.cpp test/midi_clock_slave_test.cpp + test/resampled_source.cc test/testrunner.cpp '''.split() testobj.includes = obj.includes + ['test', '../pbd'] |