mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 20:05:49 +00:00
b=815643 Add Blink's HRTFKernel to the build r=ehsan
A functional difference is that HRTFKernel is not reference-counted. --HG-- extra : rebase_source : 66a9653fed75265ec55d915b731876bcb793d513
This commit is contained in:
parent
f2b27a4c67
commit
1377d6665c
@ -96,8 +96,12 @@ bool HRTFElevation::calculateKernelsForAzimuthElevation(int azimuth, int elevati
|
||||
|
||||
// Note that depending on the fftSize returned by the panner, we may be truncating the impulse response we just loaded in.
|
||||
const size_t fftSize = HRTFPanner::fftSizeForSampleRate(sampleRate);
|
||||
kernelL = HRTFKernel::create(leftEarImpulseResponse, fftSize, sampleRate);
|
||||
kernelR = HRTFKernel::create(rightEarImpulseResponse, fftSize, sampleRate);
|
||||
MOZ_ASSERT(responseLength >= fftSize / 2);
|
||||
if (responseLength < fftSize / 2)
|
||||
return false;
|
||||
|
||||
kernelL = HRTFKernel::create(leftEarImpulseResponse, fftSize / 2, sampleRate);
|
||||
kernelR = HRTFKernel::create(rightEarImpulseResponse, fftSize / 2, sampleRate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -26,106 +26,74 @@
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if ENABLE(WEB_AUDIO)
|
||||
|
||||
#include "core/platform/audio/HRTFKernel.h"
|
||||
|
||||
#include "core/platform/FloatConversion.h"
|
||||
#include "core/platform/PlatformMemoryInstrumentation.h"
|
||||
#include "core/platform/audio/AudioChannel.h"
|
||||
#include "core/platform/audio/FFTFrame.h"
|
||||
#include <wtf/MathExtras.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include "HRTFKernel.h"
|
||||
namespace WebCore {
|
||||
|
||||
// Takes the input AudioChannel as an input impulse response and calculates the average group delay.
|
||||
// Takes the input audio channel |impulseP| as an input impulse response and calculates the average group delay.
|
||||
// This represents the initial delay before the most energetic part of the impulse response.
|
||||
// The sample-frame delay is removed from the impulseP impulse response, and this value is returned.
|
||||
// the length of the passed in AudioChannel must be a power of 2.
|
||||
static float extractAverageGroupDelay(AudioChannel* channel, size_t analysisFFTSize)
|
||||
// The sample-frame delay is removed from the |impulseP| impulse response, and this value is returned.
|
||||
// The |length| of the passed in |impulseP| must be must be a power of 2.
|
||||
static float extractAverageGroupDelay(float* impulseP, size_t length)
|
||||
{
|
||||
ASSERT(channel);
|
||||
|
||||
float* impulseP = channel->mutableData();
|
||||
|
||||
bool isSizeGood = channel->length() >= analysisFFTSize;
|
||||
ASSERT(isSizeGood);
|
||||
if (!isSizeGood)
|
||||
return 0;
|
||||
|
||||
// Check for power-of-2.
|
||||
ASSERT(1UL << static_cast<unsigned>(log2(analysisFFTSize)) == analysisFFTSize);
|
||||
MOZ_ASSERT(length && (length & (length - 1)) == 0);
|
||||
|
||||
FFTFrame estimationFrame(analysisFFTSize);
|
||||
estimationFrame.doFFT(impulseP);
|
||||
FFTBlock estimationFrame(length);
|
||||
estimationFrame.PerformFFT(impulseP);
|
||||
|
||||
float frameDelay = narrowPrecisionToFloat(estimationFrame.extractAverageGroupDelay());
|
||||
estimationFrame.doInverseFFT(impulseP);
|
||||
float frameDelay = static_cast<float>(estimationFrame.ExtractAverageGroupDelay());
|
||||
estimationFrame.PerformInverseFFT(impulseP);
|
||||
|
||||
return frameDelay;
|
||||
}
|
||||
|
||||
HRTFKernel::HRTFKernel(AudioChannel* channel, size_t fftSize, float sampleRate)
|
||||
HRTFKernel::HRTFKernel(float* impulseResponse, size_t length, float sampleRate)
|
||||
: m_frameDelay(0)
|
||||
, m_sampleRate(sampleRate)
|
||||
{
|
||||
ASSERT(channel);
|
||||
|
||||
// Determine the leading delay (average group delay) for the response.
|
||||
m_frameDelay = extractAverageGroupDelay(channel, fftSize / 2);
|
||||
m_frameDelay = extractAverageGroupDelay(impulseResponse, length);
|
||||
|
||||
float* impulseResponse = channel->mutableData();
|
||||
size_t responseLength = channel->length();
|
||||
|
||||
// We need to truncate to fit into 1/2 the FFT size (with zero padding) in order to do proper convolution.
|
||||
size_t truncatedResponseLength = min(responseLength, fftSize / 2); // truncate if necessary to max impulse response length allowed by FFT
|
||||
// The FFT size (with zero padding) needs to be twice the response length
|
||||
// in order to do proper convolution.
|
||||
unsigned fftSize = 2 * length;
|
||||
|
||||
// Quick fade-out (apply window) at truncation point
|
||||
// because the impulse response has been truncated.
|
||||
unsigned numberOfFadeOutFrames = static_cast<unsigned>(sampleRate / 4410); // 10 sample-frames @44.1KHz sample-rate
|
||||
ASSERT(numberOfFadeOutFrames < truncatedResponseLength);
|
||||
if (numberOfFadeOutFrames < truncatedResponseLength) {
|
||||
for (unsigned i = truncatedResponseLength - numberOfFadeOutFrames; i < truncatedResponseLength; ++i) {
|
||||
float x = 1.0f - static_cast<float>(i - (truncatedResponseLength - numberOfFadeOutFrames)) / numberOfFadeOutFrames;
|
||||
MOZ_ASSERT(numberOfFadeOutFrames < length);
|
||||
if (numberOfFadeOutFrames < length) {
|
||||
for (unsigned i = length - numberOfFadeOutFrames; i < length; ++i) {
|
||||
float x = 1.0f - static_cast<float>(i - (length - numberOfFadeOutFrames)) / numberOfFadeOutFrames;
|
||||
impulseResponse[i] *= x;
|
||||
}
|
||||
}
|
||||
|
||||
m_fftFrame = adoptPtr(new FFTFrame(fftSize));
|
||||
m_fftFrame->doPaddedFFT(impulseResponse, truncatedResponseLength);
|
||||
m_fftFrame = new FFTBlock(fftSize);
|
||||
m_fftFrame->PerformPaddedFFT(impulseResponse, length);
|
||||
}
|
||||
|
||||
// Interpolates two kernels with x: 0 -> 1 and returns the result.
|
||||
PassRefPtr<HRTFKernel> HRTFKernel::createInterpolatedKernel(HRTFKernel* kernel1, HRTFKernel* kernel2, float x)
|
||||
nsReturnRef<HRTFKernel> HRTFKernel::createInterpolatedKernel(HRTFKernel* kernel1, HRTFKernel* kernel2, float x)
|
||||
{
|
||||
ASSERT(kernel1 && kernel2);
|
||||
MOZ_ASSERT(kernel1 && kernel2);
|
||||
if (!kernel1 || !kernel2)
|
||||
return 0;
|
||||
return nsReturnRef<HRTFKernel>();
|
||||
|
||||
ASSERT(x >= 0.0 && x < 1.0);
|
||||
x = min(1.0f, max(0.0f, x));
|
||||
MOZ_ASSERT(x >= 0.0 && x < 1.0);
|
||||
x = mozilla::clamped(x, 0.0f, 1.0f);
|
||||
|
||||
float sampleRate1 = kernel1->sampleRate();
|
||||
float sampleRate2 = kernel2->sampleRate();
|
||||
ASSERT(sampleRate1 == sampleRate2);
|
||||
MOZ_ASSERT(sampleRate1 == sampleRate2);
|
||||
if (sampleRate1 != sampleRate2)
|
||||
return 0;
|
||||
return nsReturnRef<HRTFKernel>();
|
||||
|
||||
float frameDelay = (1 - x) * kernel1->frameDelay() + x * kernel2->frameDelay();
|
||||
|
||||
OwnPtr<FFTFrame> interpolatedFrame = FFTFrame::createInterpolatedFrame(*kernel1->fftFrame(), *kernel2->fftFrame(), x);
|
||||
return HRTFKernel::create(interpolatedFrame.release(), frameDelay, sampleRate1);
|
||||
}
|
||||
|
||||
void HRTFKernel::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
|
||||
{
|
||||
MemoryClassInfo info(memoryObjectInfo, this, PlatformMemoryTypes::AudioSharedData);
|
||||
info.addMember(m_fftFrame, "fftFrame");
|
||||
nsAutoPtr<FFTBlock> interpolatedFrame(
|
||||
FFTBlock::CreateInterpolatedBlock(*kernel1->fftFrame(), *kernel2->fftFrame(), x));
|
||||
return HRTFKernel::create(interpolatedFrame, frameDelay, sampleRate1);
|
||||
}
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
#endif // ENABLE(WEB_AUDIO)
|
||||
|
@ -29,69 +29,82 @@
|
||||
#ifndef HRTFKernel_h
|
||||
#define HRTFKernel_h
|
||||
|
||||
#include "core/platform/audio/FFTFrame.h"
|
||||
#include <wtf/OwnPtr.h>
|
||||
#include <wtf/PassOwnPtr.h>
|
||||
#include <wtf/PassRefPtr.h>
|
||||
#include <wtf/RefCounted.h>
|
||||
#include <wtf/RefPtr.h>
|
||||
#include <wtf/Vector.h>
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsAutoRef.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/FFTBlock.h"
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
class AudioChannel;
|
||||
|
||||
using mozilla::FFTBlock;
|
||||
|
||||
// HRTF stands for Head-Related Transfer Function.
|
||||
// HRTFKernel is a frequency-domain representation of an impulse-response used as part of the spatialized panning system.
|
||||
// For a given azimuth / elevation angle there will be one HRTFKernel for the left ear transfer function, and one for the right ear.
|
||||
// The leading delay (average group delay) for each impulse response is extracted:
|
||||
// m_fftFrame is the frequency-domain representation of the impulse response with the delay removed
|
||||
// m_frameDelay is the leading delay of the original impulse response.
|
||||
class HRTFKernel : public RefCounted<HRTFKernel> {
|
||||
class HRTFKernel {
|
||||
public:
|
||||
// Note: this is destructive on the passed in AudioChannel.
|
||||
// The length of channel must be a power of two.
|
||||
static PassRefPtr<HRTFKernel> create(AudioChannel* channel, size_t fftSize, float sampleRate)
|
||||
{
|
||||
return adoptRef(new HRTFKernel(channel, fftSize, sampleRate));
|
||||
}
|
||||
// Note: this is destructive on the passed in |impulseResponse|.
|
||||
// The |length| of |impulseResponse| must be a power of two.
|
||||
// The size of the DFT will be |2 * length|.
|
||||
static nsReturnRef<HRTFKernel> create(float* impulseResponse, size_t length, float sampleRate);
|
||||
|
||||
static PassRefPtr<HRTFKernel> create(PassOwnPtr<FFTFrame> fftFrame, float frameDelay, float sampleRate)
|
||||
{
|
||||
return adoptRef(new HRTFKernel(fftFrame, frameDelay, sampleRate));
|
||||
}
|
||||
static nsReturnRef<HRTFKernel> create(nsAutoPtr<FFTBlock> fftFrame, float frameDelay, float sampleRate);
|
||||
|
||||
// Given two HRTFKernels, and an interpolation factor x: 0 -> 1, returns an interpolated HRTFKernel.
|
||||
static PassRefPtr<HRTFKernel> createInterpolatedKernel(HRTFKernel* kernel1, HRTFKernel* kernel2, float x);
|
||||
static nsReturnRef<HRTFKernel> createInterpolatedKernel(HRTFKernel* kernel1, HRTFKernel* kernel2, float x);
|
||||
|
||||
FFTFrame* fftFrame() { return m_fftFrame.get(); }
|
||||
FFTBlock* fftFrame() { return m_fftFrame.get(); }
|
||||
|
||||
size_t fftSize() const { return m_fftFrame->fftSize(); }
|
||||
size_t fftSize() const { return m_fftFrame->FFTSize(); }
|
||||
float frameDelay() const { return m_frameDelay; }
|
||||
|
||||
float sampleRate() const { return m_sampleRate; }
|
||||
double nyquist() const { return 0.5 * sampleRate(); }
|
||||
|
||||
void reportMemoryUsage(MemoryObjectInfo*) const;
|
||||
|
||||
private:
|
||||
// Note: this is destructive on the passed in AudioChannel.
|
||||
HRTFKernel(AudioChannel*, size_t fftSize, float sampleRate);
|
||||
HRTFKernel(const HRTFKernel& other) MOZ_DELETE;
|
||||
void operator=(const HRTFKernel& other) MOZ_DELETE;
|
||||
|
||||
// Note: this is destructive on the passed in |impulseResponse|.
|
||||
HRTFKernel(float* impulseResponse, size_t fftSize, float sampleRate);
|
||||
|
||||
HRTFKernel(PassOwnPtr<FFTFrame> fftFrame, float frameDelay, float sampleRate)
|
||||
HRTFKernel(nsAutoPtr<FFTBlock> fftFrame, float frameDelay, float sampleRate)
|
||||
: m_fftFrame(fftFrame)
|
||||
, m_frameDelay(frameDelay)
|
||||
, m_sampleRate(sampleRate)
|
||||
{
|
||||
}
|
||||
|
||||
OwnPtr<FFTFrame> m_fftFrame;
|
||||
nsAutoPtr<FFTBlock> m_fftFrame;
|
||||
float m_frameDelay;
|
||||
float m_sampleRate;
|
||||
};
|
||||
|
||||
typedef Vector<RefPtr<HRTFKernel> > HRTFKernelList;
|
||||
typedef nsTArray<nsAutoRef<HRTFKernel> > HRTFKernelList;
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
template <>
|
||||
class nsAutoRefTraits<WebCore::HRTFKernel> :
|
||||
public nsPointerRefTraits<WebCore::HRTFKernel> {
|
||||
public:
|
||||
static void Release(WebCore::HRTFKernel* ptr) { delete(ptr); }
|
||||
};
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
inline nsReturnRef<HRTFKernel> HRTFKernel::create(float* impulseResponse, size_t length, float sampleRate)
|
||||
{
|
||||
return nsReturnRef<HRTFKernel>(new HRTFKernel(impulseResponse, length, sampleRate));
|
||||
}
|
||||
|
||||
inline nsReturnRef<HRTFKernel> HRTFKernel::create(nsAutoPtr<FFTBlock> fftFrame, float frameDelay, float sampleRate)
|
||||
{
|
||||
return nsReturnRef<HRTFKernel>(new HRTFKernel(fftFrame, frameDelay, sampleRate));
|
||||
}
|
||||
|
||||
} // namespace WebCore
|
||||
#endif // HRTFKernel_h
|
||||
|
@ -12,6 +12,7 @@ CPP_SOURCES += [
|
||||
'DynamicsCompressor.cpp',
|
||||
'DynamicsCompressorKernel.cpp',
|
||||
'FFTConvolver.cpp',
|
||||
'HRTFKernel.cpp',
|
||||
'Reverb.cpp',
|
||||
'ReverbAccumulationBuffer.cpp',
|
||||
'ReverbConvolver.cpp',
|
||||
|
Loading…
Reference in New Issue
Block a user