changeset 32:e3b32dc5180b

* Make resampler quality configurable * Fall back to linear resampling when playing very fast * Switch off transient detection in time stretcher when playing very very fast
author Chris Cannam
date Thu, 21 Sep 2006 11:17:19 +0000
parents 37af203dbd15
children 544ab25d2372
files audioio/AudioCallbackPlaySource.cpp audioio/AudioCallbackPlaySource.h audioio/PhaseVocoderTimeStretcher.cpp audioio/PhaseVocoderTimeStretcher.h main/PreferencesDialog.cpp main/PreferencesDialog.h
diffstat 6 files changed, 150 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/audioio/AudioCallbackPlaySource.cpp	Thu Sep 21 09:43:41 2006 +0000
+++ b/audioio/AudioCallbackPlaySource.cpp	Thu Sep 21 11:17:19 2006 +0000
@@ -20,6 +20,7 @@
 #include "data/model/Model.h"
 #include "view/ViewManager.h"
 #include "base/PlayParameterRepository.h"
+#include "base/Preferences.h"
 #include "data/model/DenseTimeValueModel.h"
 #include "data/model/SparseOneDimensionalModel.h"
 #include "PhaseVocoderTimeStretcher.h"
@@ -53,7 +54,9 @@
     m_outputRight(0.0),
     m_timeStretcher(0),
     m_fillThread(0),
-    m_converter(0)
+    m_converter(0),
+    m_crapConverter(0),
+    m_resampleQuality(Preferences::getInstance()->getResampleQuality())
 {
     m_viewManager->setAudioPlaySource(this);
 
@@ -67,6 +70,10 @@
     connect(PlayParameterRepository::getInstance(),
 	    SIGNAL(playParametersChanged(PlayParameters *)),
 	    this, SLOT(playParametersChanged(PlayParameters *)));
+
+    connect(Preferences::getInstance(),
+            SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
+            this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
 }
 
 AudioCallbackPlaySource::~AudioCallbackPlaySource()
@@ -168,7 +175,9 @@
     if (buffersChanged || srChanged) {
 	if (m_converter) {
 	    src_delete(m_converter);
+            src_delete(m_crapConverter);
 	    m_converter = 0;
+            m_crapConverter = 0;
 	}
     }
 
@@ -202,7 +211,9 @@
     if (m_models.empty()) {
 	if (m_converter) {
 	    src_delete(m_converter);
+            src_delete(m_crapConverter);
 	    m_converter = 0;
+            m_crapConverter = 0;
 	}
 	m_sourceSampleRate = 0;
     }
@@ -232,7 +243,9 @@
 
     if (m_converter) {
 	src_delete(m_converter);
+        src_delete(m_crapConverter);
 	m_converter = 0;
+        m_crapConverter = 0;
     }
 
     m_lastModelEndFrame = 0;
@@ -322,8 +335,10 @@
 	    }
 	}
 	if (m_converter) src_reset(m_converter);
+        if (m_crapConverter) src_reset(m_crapConverter);
     } else {
 	if (m_converter) src_reset(m_converter);
+        if (m_crapConverter) src_reset(m_crapConverter);
 	m_readBufferFill = m_writeBufferFill = startFrame;
     }
     m_mutex.unlock();
@@ -374,6 +389,14 @@
 }
 
 void
+AudioCallbackPlaySource::preferenceChanged(PropertyContainer::PropertyName n)
+{
+    if (n == "Resample Quality") {
+        setResampleQuality(Preferences::getInstance()->getResampleQuality());
+    }
+}
+
+void
 AudioCallbackPlaySource::setTargetBlockSize(size_t size)
 {
 //    std::cerr << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl;
@@ -538,29 +561,84 @@
 AudioCallbackPlaySource::setTargetSampleRate(size_t sr)
 {
     m_targetSampleRate = sr;
+    initialiseConverter();
+}
+
+void
+AudioCallbackPlaySource::initialiseConverter()
+{
+    m_mutex.lock();
+
+    if (m_converter) {
+        src_delete(m_converter);
+        src_delete(m_crapConverter);
+        m_converter = 0;
+        m_crapConverter = 0;
+    }
 
     if (getSourceSampleRate() != getTargetSampleRate()) {
 
 	int err = 0;
-	m_converter = src_new(SRC_SINC_BEST_QUALITY,
+
+	m_converter = src_new(m_resampleQuality == 2 ? SRC_SINC_BEST_QUALITY :
+                              m_resampleQuality == 1 ? SRC_SINC_MEDIUM_QUALITY :
+                              m_resampleQuality == 0 ? SRC_SINC_FASTEST :
+                                                       SRC_SINC_MEDIUM_QUALITY,
 			      getTargetChannelCount(), &err);
-	if (!m_converter) {
+
+        if (m_converter) {
+            m_crapConverter = src_new(SRC_LINEAR,
+                                      getTargetChannelCount(),
+                                      &err);
+        }
+
+	if (!m_converter || !m_crapConverter) {
 	    std::cerr
 		<< "AudioCallbackPlaySource::setModel: ERROR in creating samplerate converter: "
 		<< src_strerror(err) << std::endl;
 
+            if (m_converter) {
+                src_delete(m_converter);
+                m_converter = 0;
+            } 
+
+            if (m_crapConverter) {
+                src_delete(m_crapConverter);
+                m_crapConverter = 0;
+            }
+
+            m_mutex.unlock();
+
             emit sampleRateMismatch(getSourceSampleRate(),
                                     getTargetSampleRate(),
                                     false);
 	} else {
 
+            m_mutex.unlock();
+
             emit sampleRateMismatch(getSourceSampleRate(),
                                     getTargetSampleRate(),
                                     true);
         }
+    } else {
+        m_mutex.unlock();
     }
 }
 
+void
+AudioCallbackPlaySource::setResampleQuality(int q)
+{
+    if (q == m_resampleQuality) return;
+    m_resampleQuality = q;
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    std::cerr << "AudioCallbackPlaySource::setResampleQuality: setting to "
+              << m_resampleQuality << std::endl;
+#endif
+
+    initialiseConverter();
+}
+
 size_t
 AudioCallbackPlaySource::getTargetSampleRate() const
 {
@@ -894,8 +972,17 @@
 	data.src_ratio = ratio;
 	data.end_of_input = 0;
 	
-	int err = src_process(m_converter, &data);
-//	size_t toCopy = size_t(work * ratio + 0.1);
+	int err = 0;
+
+        if (m_timeStretcher && m_timeStretcher->getRatio() < 0.4) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+            std::cerr << "Using crappy converter" << std::endl;
+#endif
+            src_process(m_crapConverter, &data);
+        } else {
+            src_process(m_converter, &data);
+        }
+
 	size_t toCopy = size_t(got * ratio + 0.1);
 
 	if (err) {
--- a/audioio/AudioCallbackPlaySource.h	Thu Sep 21 09:43:41 2006 +0000
+++ b/audioio/AudioCallbackPlaySource.h	Thu Sep 21 11:17:19 2006 +0000
@@ -18,6 +18,7 @@
 
 #include "base/RingBuffer.h"
 #include "base/AudioPlaySource.h"
+#include "base/PropertyContainer.h"
 #include "base/Scavenger.h"
 
 #include <QObject>
@@ -177,8 +178,20 @@
      */
     size_t getSourceSamples(size_t count, float **buffer);
 
+    /**
+     * Set the time stretcher factor (i.e. playback speed).  Also
+     * specify whether the time stretcher will be variable rate
+     * (sharpening transients), and whether time stretching will be
+     * carried out on data mixed down to mono for speed.
+     */
     void setTimeStretch(float factor, bool sharpen, bool mono);
 
+    /**
+     * Set the resampler quality, 0 - 2 where 0 is fastest and 2 is
+     * highest quality.
+     */
+    void setResampleQuality(int q);
+
 signals:
     void modelReplaced();
 
@@ -191,6 +204,7 @@
     void playLoopModeChanged();
     void playSelectionModeChanged();
     void playParametersChanged(PlayParameters *);
+    void preferenceChanged(PropertyContainer::PropertyName);
 
 protected:
     ViewManager                     *m_viewManager;
@@ -274,6 +288,9 @@
     QWaitCondition m_condition;
     AudioCallbackPlaySourceFillThread *m_fillThread;
     SRC_STATE *m_converter;
+    SRC_STATE *m_crapConverter; // for use when playing very fast
+    int m_resampleQuality;
+    void initialiseConverter();
 };
 
 #endif
--- a/audioio/PhaseVocoderTimeStretcher.cpp	Thu Sep 21 09:43:41 2006 +0000
+++ b/audioio/PhaseVocoderTimeStretcher.cpp	Thu Sep 21 11:17:19 2006 +0000
@@ -38,7 +38,6 @@
     m_mutex(new QMutex())
 {
     initialise();
-
 }
 
 PhaseVocoderTimeStretcher::~PhaseVocoderTimeStretcher()
@@ -144,7 +143,7 @@
         } else {
             m_n1 = 256;
         }
-        if (m_sharpen) {
+        if (shouldSharpen()) {
             m_wlen = 2048;
         }
         m_n2 = lrintf(m_n1 * m_ratio);
@@ -158,7 +157,7 @@
         } else {
             m_n2 = 256;
         }
-        if (m_sharpen) {
+        if (shouldSharpen()) {
             if (m_wlen < 2048) m_wlen = 2048;
         }
         m_n1 = lrintf(m_n2 / m_ratio);
@@ -318,11 +317,12 @@
 	writable = std::min(writable, samples - consumed);
 
 	if (writable == 0) {
-	    //!!! then what? I don't think this should happen, but
+#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER
 	    std::cerr << "WARNING: PhaseVocoderTimeStretcher::putInput: writable == 0 (inbuf has " << m_inbuf[0]->getReadSpace() << " samples available for reading, space for " << m_inbuf[0]->getWriteSpace() << " more)" << std::endl;
+#endif
             if (m_inbuf[0]->getReadSpace() < m_wlen ||
                 m_outbuf[0]->getWriteSpace() < m_n2) {
-                std::cerr << "Outbuf has space for " << m_outbuf[0]->getWriteSpace() << " (n2 = " << m_n2 << "), won't be able to process" << std::endl;
+                std::cerr << "WARNING: PhaseVocoderTimeStretcher::putInput: Inbuf has " << m_inbuf[0]->getReadSpace() << ", outbuf has space for " << m_outbuf[0]->getWriteSpace() << " (n2 = " << m_n2 << ", wlen = " << m_wlen << "), won't be able to process" << std::endl;
                 break;
             }
 	} else {
@@ -354,7 +354,7 @@
             }
 
             bool transient = false;
-            if (m_sharpen) transient = isTransient();
+            if (shouldSharpen()) transient = isTransient();
 
             size_t n2 = m_n2;
 
@@ -380,9 +380,11 @@
                 
                 n2 = lrintf(idealSquashy / squashyCount);
 
+#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER
                 if (n2 != m_n2) {
                     std::cerr << m_n2 << " -> " << n2 << std::endl;
                 }
+#endif
             }
 
             for (size_t c = 0; c < m_channels; ++c) {
--- a/audioio/PhaseVocoderTimeStretcher.h	Thu Sep 21 09:43:41 2006 +0000
+++ b/audioio/PhaseVocoderTimeStretcher.h	Thu Sep 21 11:17:19 2006 +0000
@@ -143,6 +143,10 @@
     void calculateParameters();
     void cleanup();
 
+    bool shouldSharpen() {
+        return m_sharpen && (m_ratio > 0.25);
+    }
+
     size_t m_sampleRate;
     size_t m_channels;
     size_t m_maxOutputBlockSize;
--- a/main/PreferencesDialog.cpp	Thu Sep 21 09:43:41 2006 +0000
+++ b/main/PreferencesDialog.cpp	Thu Sep 21 11:17:19 2006 +0000
@@ -94,6 +94,20 @@
     connect(frequency, SIGNAL(valueChanged(double)),
             this, SLOT(tuningFrequencyChanged(double)));
 
+    QComboBox *resampleQuality = new QComboBox;
+
+    int rsq = prefs->getPropertyRangeAndValue("Resample Quality", &min, &max);
+    m_resampleQuality = rsq;
+
+    for (i = min; i <= max; ++i) {
+        resampleQuality->addItem(prefs->getPropertyValueLabel("Resample Quality", i));
+    }
+
+    resampleQuality->setCurrentIndex(rsq);
+
+    connect(resampleQuality, SIGNAL(currentIndexChanged(int)),
+            this, SLOT(resampleQualityChanged(int)));
+
     int row = 0;
 
     subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel
@@ -106,6 +120,11 @@
                        row, 0);
     subgrid->addWidget(frequency, row++, 1, 1, 2);
 
+    subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel
+                                                ("Resample Quality"))),
+                       row, 0);
+    subgrid->addWidget(resampleQuality, row++, 1, 1, 2);
+
     subgrid->addWidget(new QLabel(prefs->getPropertyLabel
                                   ("Smooth Spectrogram")),
                        row, 0, 1, 2);
@@ -168,6 +187,13 @@
 }
 
 void
+PreferencesDialog::resampleQualityChanged(int q)
+{
+    m_resampleQuality = q;
+    m_applyButton->setEnabled(true);
+}
+
+void
 PreferencesDialog::okClicked()
 {
     applyClicked();
@@ -183,6 +209,7 @@
     prefs->setPropertyBoxLayout(Preferences::PropertyBoxLayout
                                 (m_propertyLayout));
     prefs->setTuningFrequency(m_tuningFrequency);
+    prefs->setResampleQuality(m_resampleQuality);
     m_applyButton->setEnabled(false);
 }    
 
--- a/main/PreferencesDialog.h	Thu Sep 21 09:43:41 2006 +0000
+++ b/main/PreferencesDialog.h	Thu Sep 21 11:17:19 2006 +0000
@@ -36,6 +36,7 @@
     void smoothSpectrogramChanged(int state);
     void propertyLayoutChanged(int layout);
     void tuningFrequencyChanged(double freq);
+    void resampleQualityChanged(int quality);
 
     void okClicked();
     void applyClicked();
@@ -49,6 +50,7 @@
     bool  m_smoothSpectrogram;
     int   m_propertyLayout;
     float m_tuningFrequency;
+    int   m_resampleQuality;
 };
 
 #endif