summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/ardour/ardour/audiosource.h3
-rw-r--r--libs/ardour/ardour/importable_source.h2
-rw-r--r--libs/ardour/ardour/resampled_source.h8
-rw-r--r--libs/ardour/ardour/silentfilesource.h2
-rw-r--r--libs/ardour/ardour/sndfileimportable.h1
-rw-r--r--libs/ardour/ardour/sndfilesource.h2
-rw-r--r--libs/ardour/import.cc50
-rw-r--r--libs/ardour/resampled_source.cc60
-rw-r--r--libs/ardour/sndfileimportable.cc8
-rw-r--r--libs/ardour/sndfilesource.cc7
-rw-r--r--libs/ardour/test/resampled_source.cc31
-rw-r--r--libs/ardour/test/resampled_source.h12
-rwxr-xr-xlibs/ardour/test/test.wavbin0 -> 17720 bytes
-rw-r--r--libs/ardour/wscript1
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
new file mode 100755
index 0000000000..369531cc91
--- /dev/null
+++ b/libs/ardour/test/test.wav
Binary files differ
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']