fune/dom/media/webaudio/blink/HRTFElevation.cpp
Cristian Tuns 5d6b51256b Backed out 57 changesets (bug 1839389, bug 1840869, bug 1840399, bug 1840402, bug 1823953, bug 1828912, bug 1826382, bug 1837160, bug 1839391, bug 1833654) for causing build bustages in ogg_<something> CLOSED TREE
Backed out changeset 61356e1447e3 (bug 1823953)
Backed out changeset 85785505b6d6 (bug 1823953)
Backed out changeset 46a61cbfe8a8 (bug 1833654)
Backed out changeset 83e3de80337b (bug 1833654)
Backed out changeset 1a10c12874ac (bug 1840399)
Backed out changeset 6b087145b67f (bug 1833654)
Backed out changeset b9ac857ad43f (bug 1840399)
Backed out changeset 4b841e8dd033 (bug 1823953)
Backed out changeset 650e35803834 (bug 1823953)
Backed out changeset c11b58ac0709 (bug 1823953)
Backed out changeset c0249c90bc31 (bug 1823953)
Backed out changeset 8929288d5aec (bug 1823953)
Backed out changeset 828792b886bd (bug 1823953)
Backed out changeset 873f1d4a8875 (bug 1840869)
Backed out changeset a25abd05302c (bug 1823953)
Backed out changeset d4b1eb442c36 (bug 1840399)
Backed out changeset c25509d72a96 (bug 1840399)
Backed out changeset 0f72a0626a28 (bug 1840402)
Backed out changeset 82e7574364ce (bug 1840399)
Backed out changeset 93073105f063 (bug 1840399)
Backed out changeset 56ec8e3405e9 (bug 1840399)
Backed out changeset ff15dad37ab8 (bug 1840399)
Backed out changeset 0655ebd61eda (bug 1840399)
Backed out changeset 7bca1ae06c7d (bug 1828912)
Backed out changeset 8a5a849cfe5f (bug 1828912)
Backed out changeset 3d8422a2038a (bug 1828912)
Backed out changeset f08ee5de9370 (bug 1823953)
Backed out changeset a4eb210620ff (bug 1823953)
Backed out changeset aa8914cd55be (bug 1839391)
Backed out changeset 3ea1f43e4024 (bug 1823953)
Backed out changeset 3efe02ffa1c8 (bug 1826382)
Backed out changeset 81c4553ec23d (bug 1839391)
Backed out changeset 130894e4a781 (bug 1839391)
Backed out changeset 9a0247b0fc85 (bug 1839391)
Backed out changeset 11a923064382 (bug 1839391)
Backed out changeset 98ffb66160c3 (bug 1837160)
Backed out changeset a80dda9a220a (bug 1837160)
Backed out changeset 251b4ef97a2b (bug 1837160)
Backed out changeset 7372632eb32f (bug 1837160)
Backed out changeset c5d54bc3ee26 (bug 1839389)
Backed out changeset b232ec1bbc2d (bug 1833654)
Backed out changeset fc7ba125c2fe (bug 1833654)
Backed out changeset 8a47f6882e61 (bug 1823953)
Backed out changeset e29810541b53 (bug 1828912)
Backed out changeset bcf10730c8c9 (bug 1828912)
Backed out changeset 8df8290b6c33 (bug 1826382)
Backed out changeset 2811d12803cf (bug 1826382)
Backed out changeset 3fc718561ec9 (bug 1826382)
Backed out changeset 7827183776e1 (bug 1823953)
Backed out changeset a3eb5f228d9a (bug 1826382)
Backed out changeset 3113ad2e0987 (bug 1823953)
Backed out changeset 4b1dc01525af (bug 1823953)
Backed out changeset f7f4a7585ceb (bug 1823953)
Backed out changeset 93042f1becec (bug 1823953)
Backed out changeset b9ca30a0a066 (bug 1823953)
Backed out changeset 1000c4a6a92a (bug 1823953)
Backed out changeset 05dc13775fd6 (bug 1823953)
2023-08-01 09:37:39 -04:00

338 lines
13 KiB
C++

/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* 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 "HRTFElevation.h"
#include <speex/speex_resampler.h>
#include "mozilla/PodOperations.h"
#include "AudioSampleFormat.h"
#include "IRC_Composite_C_R0195-incl.cpp"
using namespace mozilla;
namespace WebCore {
const int elevationSpacing = irc_composite_c_r0195_elevation_interval;
const int firstElevation = irc_composite_c_r0195_first_elevation;
const int numberOfElevations = MOZ_ARRAY_LENGTH(irc_composite_c_r0195);
const unsigned HRTFElevation::NumberOfTotalAzimuths = 360 / 15 * 8;
const int rawSampleRate = irc_composite_c_r0195_sample_rate;
// Number of frames in an individual impulse response.
const size_t ResponseFrameSize = 256;
size_t HRTFElevation::sizeOfIncludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const {
size_t amount = aMallocSizeOf(this);
amount += m_kernelListL.ShallowSizeOfExcludingThis(aMallocSizeOf);
for (size_t i = 0; i < m_kernelListL.Length(); i++) {
amount += m_kernelListL[i]->sizeOfIncludingThis(aMallocSizeOf);
}
return amount;
}
size_t HRTFElevation::fftSizeForSampleRate(float sampleRate) {
// The IRCAM HRTF impulse responses were 512 sample-frames @44.1KHz,
// but these have been truncated to 256 samples.
// An FFT-size of twice impulse response size is used (for convolution).
// So for sample rates of 44.1KHz an FFT size of 512 is good.
// We double the FFT-size only for sample rates at least double this.
// If the FFT size is too large then the impulse response will be padded
// with zeros without the fade-out provided by HRTFKernel.
MOZ_ASSERT(sampleRate > 1.0 && sampleRate < 1048576.0);
// This is the size if we were to use all raw response samples.
unsigned resampledLength =
floorf(ResponseFrameSize * sampleRate / rawSampleRate);
// Keep things semi-sane, with max FFT size of 1024.
unsigned size = std::min(resampledLength, 1023U);
// Ensure a minimum of 2 * WEBAUDIO_BLOCK_SIZE (with the size++ below) for
// FFTConvolver and set the 8 least significant bits for rounding up to
// the next power of 2 below.
size |= 2 * WEBAUDIO_BLOCK_SIZE - 1;
// Round up to the next power of 2, making the FFT size no more than twice
// the impulse response length. This doubles size for values that are
// already powers of 2. This works by filling in alls bit to right of the
// most significant bit. The most significant bit is no greater than
// 1 << 9, and the least significant 8 bits were already set above, so
// there is at most one bit to add.
size |= (size >> 1);
size++;
MOZ_ASSERT((size & (size - 1)) == 0);
return size;
}
nsReturnRef<HRTFKernel> HRTFElevation::calculateKernelForAzimuthElevation(
int azimuth, int elevation, SpeexResamplerState* resampler,
float sampleRate) {
int elevationIndex = (elevation - firstElevation) / elevationSpacing;
MOZ_ASSERT(elevationIndex >= 0 && elevationIndex <= numberOfElevations);
int numberOfAzimuths = irc_composite_c_r0195[elevationIndex].count;
int azimuthSpacing = 360 / numberOfAzimuths;
MOZ_ASSERT(numberOfAzimuths * azimuthSpacing == 360);
int azimuthIndex = azimuth / azimuthSpacing;
MOZ_ASSERT(azimuthIndex * azimuthSpacing == azimuth);
const int16_t(&impulse_response_data)[ResponseFrameSize] =
irc_composite_c_r0195[elevationIndex].azimuths[azimuthIndex];
// When libspeex_resampler is compiled with FIXED_POINT, samples in
// speex_resampler_process_float are rounded directly to int16_t, which
// only works well if the floats are in the range +/-32767. On such
// platforms it's better to resample before converting to float anyway.
#ifdef MOZ_SAMPLE_TYPE_S16
# define RESAMPLER_PROCESS speex_resampler_process_int
const int16_t* response = impulse_response_data;
const int16_t* resampledResponse;
#else
# define RESAMPLER_PROCESS speex_resampler_process_float
float response[ResponseFrameSize];
ConvertAudioSamples(impulse_response_data, response, ResponseFrameSize);
float* resampledResponse;
#endif
// Note that depending on the fftSize returned by the panner, we may be
// truncating the impulse response.
const size_t resampledResponseLength = fftSizeForSampleRate(sampleRate) / 2;
AutoTArray<AudioDataValue, 2 * ResponseFrameSize> resampled;
if (sampleRate == rawSampleRate) {
resampledResponse = response;
MOZ_ASSERT(resampledResponseLength == ResponseFrameSize);
} else {
resampled.SetLength(resampledResponseLength);
resampledResponse = resampled.Elements();
speex_resampler_skip_zeros(resampler);
// Feed the input buffer into the resampler.
spx_uint32_t in_len = ResponseFrameSize;
spx_uint32_t out_len = resampled.Length();
RESAMPLER_PROCESS(resampler, 0, response, &in_len, resampled.Elements(),
&out_len);
if (out_len < resampled.Length()) {
// The input should have all been processed.
MOZ_ASSERT(in_len == ResponseFrameSize);
// Feed in zeros get the data remaining in the resampler.
spx_uint32_t out_index = out_len;
in_len = speex_resampler_get_input_latency(resampler);
out_len = resampled.Length() - out_index;
RESAMPLER_PROCESS(resampler, 0, nullptr, &in_len,
resampled.Elements() + out_index, &out_len);
out_index += out_len;
// There may be some uninitialized samples remaining for very low
// sample rates.
PodZero(resampled.Elements() + out_index, resampled.Length() - out_index);
}
speex_resampler_reset_mem(resampler);
}
#ifdef MOZ_SAMPLE_TYPE_S16
AutoTArray<float, 2 * ResponseFrameSize> floatArray;
floatArray.SetLength(resampledResponseLength);
float* floatResponse = floatArray.Elements();
ConvertAudioSamples(resampledResponse, floatResponse,
resampledResponseLength);
#else
float* floatResponse = resampledResponse;
#endif
#undef RESAMPLER_PROCESS
return HRTFKernel::create(floatResponse, resampledResponseLength, sampleRate);
}
// The range of elevations for the IRCAM impulse responses varies depending on
// azimuth, but the minimum elevation appears to always be -45.
//
// Here's how it goes:
static int maxElevations[] = {
// Azimuth
//
90, // 0
45, // 15
60, // 30
45, // 45
75, // 60
45, // 75
60, // 90
45, // 105
75, // 120
45, // 135
60, // 150
45, // 165
75, // 180
45, // 195
60, // 210
45, // 225
75, // 240
45, // 255
60, // 270
45, // 285
75, // 300
45, // 315
60, // 330
45 // 345
};
nsReturnRef<HRTFElevation> HRTFElevation::createBuiltin(int elevation,
float sampleRate) {
if (elevation < firstElevation ||
elevation > firstElevation + numberOfElevations * elevationSpacing ||
(elevation / elevationSpacing) * elevationSpacing != elevation)
return nsReturnRef<HRTFElevation>();
// Spacing, in degrees, between every azimuth loaded from resource.
// Some elevations do not have data for all these intervals.
// See maxElevations.
static const unsigned AzimuthSpacing = 15;
static const unsigned NumberOfRawAzimuths = 360 / AzimuthSpacing;
static_assert(AzimuthSpacing * NumberOfRawAzimuths == 360, "Not a multiple");
static const unsigned InterpolationFactor =
NumberOfTotalAzimuths / NumberOfRawAzimuths;
static_assert(
NumberOfTotalAzimuths == NumberOfRawAzimuths * InterpolationFactor,
"Not a multiple");
HRTFKernelList kernelListL;
kernelListL.SetLength(NumberOfTotalAzimuths);
SpeexResamplerState* resampler =
sampleRate == rawSampleRate
? nullptr
: speex_resampler_init(1, rawSampleRate, sampleRate,
SPEEX_RESAMPLER_QUALITY_MIN, nullptr);
// Load convolution kernels from HRTF files.
int interpolatedIndex = 0;
for (unsigned rawIndex = 0; rawIndex < NumberOfRawAzimuths; ++rawIndex) {
// Don't let elevation exceed maximum for this azimuth.
int maxElevation = maxElevations[rawIndex];
int actualElevation = std::min(elevation, maxElevation);
kernelListL[interpolatedIndex] = calculateKernelForAzimuthElevation(
rawIndex * AzimuthSpacing, actualElevation, resampler, sampleRate);
interpolatedIndex += InterpolationFactor;
}
if (resampler) speex_resampler_destroy(resampler);
// Now go back and interpolate intermediate azimuth values.
for (unsigned i = 0; i < NumberOfTotalAzimuths; i += InterpolationFactor) {
int j = (i + InterpolationFactor) % NumberOfTotalAzimuths;
// Create the interpolated convolution kernels and delays.
for (unsigned jj = 1; jj < InterpolationFactor; ++jj) {
float x =
float(jj) / float(InterpolationFactor); // interpolate from 0 -> 1
kernelListL[i + jj] = HRTFKernel::createInterpolatedKernel(
kernelListL[i], kernelListL[j], x);
}
}
return nsReturnRef<HRTFElevation>(
new HRTFElevation(std::move(kernelListL), elevation, sampleRate));
}
nsReturnRef<HRTFElevation> HRTFElevation::createByInterpolatingSlices(
HRTFElevation* hrtfElevation1, HRTFElevation* hrtfElevation2, float x,
float sampleRate) {
MOZ_ASSERT(hrtfElevation1 && hrtfElevation2);
if (!hrtfElevation1 || !hrtfElevation2) return nsReturnRef<HRTFElevation>();
MOZ_ASSERT(x >= 0.0 && x < 1.0);
HRTFKernelList kernelListL;
kernelListL.SetLength(NumberOfTotalAzimuths);
const HRTFKernelList& kernelListL1 = hrtfElevation1->kernelListL();
const HRTFKernelList& kernelListL2 = hrtfElevation2->kernelListL();
// Interpolate kernels of corresponding azimuths of the two elevations.
for (unsigned i = 0; i < NumberOfTotalAzimuths; ++i) {
kernelListL[i] = HRTFKernel::createInterpolatedKernel(kernelListL1[i],
kernelListL2[i], x);
}
// Interpolate elevation angle.
double angle = (1.0 - x) * hrtfElevation1->elevationAngle() +
x * hrtfElevation2->elevationAngle();
return nsReturnRef<HRTFElevation>(new HRTFElevation(
std::move(kernelListL), static_cast<int>(angle), sampleRate));
}
void HRTFElevation::getKernelsFromAzimuth(
double azimuthBlend, unsigned azimuthIndex, HRTFKernel*& kernelL,
HRTFKernel*& kernelR, double& frameDelayL, double& frameDelayR) {
bool checkAzimuthBlend = azimuthBlend >= 0.0 && azimuthBlend < 1.0;
MOZ_ASSERT(checkAzimuthBlend);
if (!checkAzimuthBlend) azimuthBlend = 0.0;
unsigned numKernels = m_kernelListL.Length();
bool isIndexGood = azimuthIndex < numKernels;
MOZ_ASSERT(isIndexGood);
if (!isIndexGood) {
kernelL = 0;
kernelR = 0;
return;
}
// Return the left and right kernels,
// using symmetry to produce the right kernel.
kernelL = m_kernelListL[azimuthIndex];
int azimuthIndexR = (numKernels - azimuthIndex) % numKernels;
kernelR = m_kernelListL[azimuthIndexR];
frameDelayL = kernelL->frameDelay();
frameDelayR = kernelR->frameDelay();
int azimuthIndex2L = (azimuthIndex + 1) % numKernels;
double frameDelay2L = m_kernelListL[azimuthIndex2L]->frameDelay();
int azimuthIndex2R = (numKernels - azimuthIndex2L) % numKernels;
double frameDelay2R = m_kernelListL[azimuthIndex2R]->frameDelay();
// Linearly interpolate delays.
frameDelayL =
(1.0 - azimuthBlend) * frameDelayL + azimuthBlend * frameDelay2L;
frameDelayR =
(1.0 - azimuthBlend) * frameDelayR + azimuthBlend * frameDelay2R;
}
} // namespace WebCore