Bug 1154043 - (part 2) Build band limited tables after fundamental frequency is known r=dminor
authorDan Minor <dminor@mozilla.com>
Fri, 13 Nov 2015 11:17:39 -0500
changeset 326864 82d122e1b8c3dc52799fb830452c18b9cd267b3d
parent 326863 107fb1f539dad7ea2ee66f7c29e43817879344b2
child 326865 f07c956c84eae98d012b0f16293464d5779bbd57
push id10169
push userdminor@mozilla.com
push dateThu, 28 Jan 2016 13:10:48 +0000
reviewersdminor
bugs1154043
milestone45.0a1
Bug 1154043 - (part 2) Build band limited tables after fundamental frequency is known r=dminor If we build the band limited tables after the fundamental frequency is known, we can exclude partials that are above the nyquist frequency for the sampling frequency being used. We rebuild the band limited tables each time we see a request for data for a lower fundamental frequency so we have the required partials.
dom/media/webaudio/blink/PeriodicWave.cpp
dom/media/webaudio/blink/PeriodicWave.h
--- a/dom/media/webaudio/blink/PeriodicWave.cpp
+++ b/dom/media/webaudio/blink/PeriodicWave.cpp
@@ -24,16 +24,17 @@
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "PeriodicWave.h"
 #include <algorithm>
 #include <cmath>
+#include <limits>
 #include "mozilla/FFTBlock.h"
 
 const unsigned MinPeriodicWaveSize = 4096; // This must be a power of two.
 const unsigned MaxPeriodicWaveSize = 8192; // This must be a power of two.
 const float CentsPerRange = 1200 / 3; // 1/3 Octave.
 
 using namespace mozilla;
 using mozilla::dom::OscillatorType;
@@ -59,17 +60,16 @@ PeriodicWave::create(float sampleRate,
         periodicWave->m_numberOfComponents = numberOfComponents;
         periodicWave->m_realComponents = new AudioFloatArray(numberOfComponents);
         periodicWave->m_imagComponents = new AudioFloatArray(numberOfComponents);
         memcpy(periodicWave->m_realComponents->Elements(), real,
                numberOfComponents * sizeof(float));
         memcpy(periodicWave->m_imagComponents->Elements(), imag,
                numberOfComponents * sizeof(float));
 
-        periodicWave->createBandLimitedTables();
         return periodicWave.forget();
     }
     return nullptr;
 }
 
 already_AddRefed<PeriodicWave>
 PeriodicWave::createSine(float sampleRate)
 {
@@ -104,27 +104,29 @@ PeriodicWave::createTriangle(float sampl
         new PeriodicWave(sampleRate, MinPeriodicWaveSize);
     periodicWave->generateBasicWaveform(OscillatorType::Triangle);
     return periodicWave.forget();
 }
 
 PeriodicWave::PeriodicWave(float sampleRate, size_t numberOfComponents)
     : m_sampleRate(sampleRate)
     , m_centsPerRange(CentsPerRange)
+    , m_lowestRequestedFundamentalFrequency(std::numeric_limits<float>::max())
 {
     float nyquist = 0.5 * m_sampleRate;
 
     if (numberOfComponents <= MinPeriodicWaveSize) {
         m_periodicWaveSize = MinPeriodicWaveSize;
     } else {
         unsigned npow2 = powf(2.0f, floorf(logf(numberOfComponents - 1.0)/logf(2.0f) + 1.0f));
         m_periodicWaveSize = std::min(MaxPeriodicWaveSize, npow2);
     }
 
     m_numberOfRanges = (unsigned)(3.0f*logf(m_periodicWaveSize)/logf(2.0f));
+    m_bandLimitedTables.SetCapacity(m_numberOfRanges);
     m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
     m_rateScale = m_periodicWaveSize / m_sampleRate;
 }
 
 size_t PeriodicWave::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
     size_t amount = aMallocSizeOf(this);
 
@@ -135,20 +137,26 @@ size_t PeriodicWave::sizeOfIncludingThis
         }
     }
 
     return amount;
 }
 
 void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor)
 {
+
     // Negative frequencies are allowed, in which case we alias
     // to the positive frequency.
     fundamentalFrequency = fabsf(fundamentalFrequency);
 
+    if (fundamentalFrequency < m_lowestRequestedFundamentalFrequency) {
+        createBandLimitedTables(fundamentalFrequency);
+        m_lowestRequestedFundamentalFrequency = fundamentalFrequency;
+    }
+
     // Calculate the pitch range.
     float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5;
     float centsAboveLowestFrequency = logf(ratio)/logf(2.0f) * 1200;
 
     // Add one to round-up to the next range just in time to truncate
     // partials before aliasing occurs.
     float pitchRange = 1 + centsAboveLowestFrequency / m_centsPerRange;
 
@@ -187,39 +195,44 @@ unsigned PeriodicWave::numberOfPartialsF
 
     return numberOfPartials;
 }
 
 // Convert into time-domain wave buffers.
 // One table is created for each range for non-aliasing playback
 // at different playback rates. Thus, higher ranges have more
 // high-frequency partials culled out.
-void PeriodicWave::createBandLimitedTables()
+void PeriodicWave::createBandLimitedTables(float fundamentalFrequency)
 {
     float normalizationScale = 1;
 
     unsigned fftSize = m_periodicWaveSize;
     unsigned i;
 
     const float *realData = m_realComponents->Elements();
     const float *imagData = m_imagComponents->Elements();
 
-    m_bandLimitedTables.SetCapacity(m_numberOfRanges);
+    m_bandLimitedTables.Clear();
 
     for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) {
         // This FFTBlock is used to cull partials (represented by frequency bins).
         FFTBlock frame(fftSize);
 
         // Find the starting bin where we should start culling the aliasing
         // partials for this pitch range.  We need to clear out the highest
         // frequencies to band-limit the waveform.
         unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex);
         // Also limit to the number of components that are provided.
         numberOfPartials = std::min(numberOfPartials, m_numberOfComponents - 1);
 
+        // Limit number of partials to those below Nyquist frequency
+        float nyquist = 0.5 * m_sampleRate;
+        numberOfPartials = std::min(numberOfPartials,
+                                    (unsigned)(nyquist / fundamentalFrequency));
+
         // Copy from loaded frequency data and generate complex conjugate
         // because of the way the inverse FFT is defined.
         // The coefficients of higher partials remain zero, as initialized in
         // the FFTBlock constructor.
         for (i = 0; i < numberOfPartials + 1; ++i) {
             frame.RealData(i) = realData[i];
             frame.ImagData(i) = -imagData[i];
         }
@@ -312,13 +325,11 @@ void PeriodicWave::generateBasicWaveform
             a = 0;
             b = 0;
             break;
         }
 
         realP[n] = a;
         imagP[n] = b;
     }
-
-    createBandLimitedTables();
 }
 
 } // namespace WebCore
--- a/dom/media/webaudio/blink/PeriodicWave.h
+++ b/dom/media/webaudio/blink/PeriodicWave.h
@@ -100,15 +100,16 @@ private:
     unsigned numberOfRanges() const { return m_numberOfRanges; }
 
     // Maximum possible number of partials (before culling).
     unsigned maxNumberOfPartials() const;
 
     unsigned numberOfPartialsForRange(unsigned rangeIndex) const;
 
     // Creates tables based on numberOfComponents Fourier coefficients.
-    void createBandLimitedTables();
+    void createBandLimitedTables(float fundamentalFrequency);
+    float m_lowestRequestedFundamentalFrequency;
     nsTArray<nsAutoPtr<AlignedAudioFloatArray> > m_bandLimitedTables;
 };
 
 } // namespace WebCore
 
 #endif // PeriodicWave_h