Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate) { float scale = 1; if (normalize) { scale = calculateNormalizationScale(impulseResponse, impulseResponseBufferLength, sampleRate); if (scale) { for (uint32_t i = 0; i < impulseResponse->GetChannels(); ++i) { AudioBufferInPlaceScale(const_cast<float*>(impulseResponse->GetData(i)), 1, scale, impulseResponseBufferLength); } } } initialize(impulseResponse, impulseResponseBufferLength, renderSliceSize, maxFFTSize, numberOfChannels, useBackgroundThreads); // Undo scaling since this shouldn't be a destructive operation on impulseResponse. // FIXME: What about roundoff? Perhaps consider making a temporary scaled copy // instead of scaling and unscaling in place. if (normalize && scale) { for (uint32_t i = 0; i < impulseResponse->GetChannels(); ++i) { AudioBufferInPlaceScale(const_cast<float*>(impulseResponse->GetData(i)), 1, 1 / scale, impulseResponseBufferLength); } } }
void AudioBufferInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE], uint32_t aChannelCount, float aScale) { AudioBufferInPlaceScale(aBlock, aChannelCount, aScale, WEBAUDIO_BLOCK_SIZE); }
void AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE], float aScale) { AudioBufferInPlaceScale(aBlock, aScale, WEBAUDIO_BLOCK_SIZE); }
// 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(const float* realData, const float* imagData, unsigned numberOfComponents) { float normalizationScale = 1; unsigned fftSize = m_periodicWaveSize; unsigned halfSize = fftSize / 2 + 1; unsigned i; numberOfComponents = std::min(numberOfComponents, halfSize); m_bandLimitedTables.SetCapacity(m_numberOfRanges); for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) { // This FFTBlock is used to cull partials (represented by frequency bins). FFTBlock frame(fftSize); nsAutoArrayPtr<float> realP(new float[halfSize]); nsAutoArrayPtr<float> imagP(new float[halfSize]); // Copy from loaded frequency data and scale. float scale = fftSize; AudioBufferCopyWithScale(realData, scale, realP, numberOfComponents); AudioBufferCopyWithScale(imagData, scale, imagP, numberOfComponents); // If fewer components were provided than 1/2 FFT size, // then clear the remaining bins. for (i = numberOfComponents; i < halfSize; ++i) { realP[i] = 0; imagP[i] = 0; } // Generate complex conjugate because of the way the // inverse FFT is defined. float minusOne = -1; AudioBufferInPlaceScale(imagP, minusOne, halfSize); // Find the starting bin where we should start culling. // We need to clear out the highest frequencies to band-limit // the waveform. unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex); // Cull the aliasing partials for this pitch range. for (i = numberOfPartials + 1; i < halfSize; ++i) { realP[i] = 0; imagP[i] = 0; } // Clear nyquist if necessary. if (numberOfPartials < halfSize) realP[halfSize-1] = 0; // Clear any DC-offset. realP[0] = 0; // Clear values which have no effect. imagP[0] = 0; imagP[halfSize-1] = 0; // Create the band-limited table. AudioFloatArray* table = new AudioFloatArray(m_periodicWaveSize); m_bandLimitedTables.AppendElement(table); // Apply an inverse FFT to generate the time-domain table data. float* data = m_bandLimitedTables[rangeIndex]->Elements(); frame.PerformInverseFFT(realP, imagP, data); // For the first range (which has the highest power), calculate // its peak value then compute normalization scale. if (!rangeIndex) { float maxValue; maxValue = AudioBufferPeakValue(data, m_periodicWaveSize); if (maxValue) normalizationScale = 1.0f / maxValue; } // Apply normalization scale. AudioBufferInPlaceScale(data, normalizationScale, m_periodicWaveSize); } }