Remove all 'std' using directives from WebCore
[WebKit-https.git] / Source / WebCore / platform / audio / HRTFElevation.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30
31 #if ENABLE(WEB_AUDIO)
32
33 #include "HRTFElevation.h"
34
35 #include "AudioBus.h"
36 #include "AudioFileReader.h"
37 #include "Biquad.h"
38 #include "FFTFrame.h"
39 #include "HRTFDatabaseLoader.h"
40 #include "HRTFPanner.h"
41 #include <algorithm>
42 #include <math.h>
43 #include <wtf/OwnPtr.h>
44
45
46 namespace WebCore {
47
48 const unsigned HRTFElevation::AzimuthSpacing = 15;
49 const unsigned HRTFElevation::NumberOfRawAzimuths = 360 / AzimuthSpacing;
50 const unsigned HRTFElevation::InterpolationFactor = 8;
51 const unsigned HRTFElevation::NumberOfTotalAzimuths = NumberOfRawAzimuths * InterpolationFactor;
52
53 // Total number of components of an HRTF database.
54 const size_t TotalNumberOfResponses = 240;
55
56 // Number of frames in an individual impulse response.
57 const size_t ResponseFrameSize = 256;
58
59 // Sample-rate of the spatialization impulse responses as stored in the resource file.
60 // The impulse responses may be resampled to a different sample-rate (depending on the audio hardware) when they are loaded.
61 const float ResponseSampleRate = 44100;
62
63 #if PLATFORM(MAC) || USE(WEBAUDIO_GSTREAMER) || PLATFORM(NIX)
64 #define USE_CONCATENATED_IMPULSE_RESPONSES
65 #endif
66
67 #ifdef USE_CONCATENATED_IMPULSE_RESPONSES
68 // Lazily load a concatenated HRTF database for given subject and store it in a
69 // local hash table to ensure quick efficient future retrievals.
70 static AudioBus* getConcatenatedImpulseResponsesForSubject(const String& subjectName)
71 {
72     typedef HashMap<String, AudioBus*> AudioBusMap;
73     DEFINE_STATIC_LOCAL(AudioBusMap, audioBusMap, ());
74
75     AudioBus* bus;
76     AudioBusMap::iterator iterator = audioBusMap.find(subjectName);
77     if (iterator == audioBusMap.end()) {
78         RefPtr<AudioBus> concatenatedImpulseResponses = AudioBus::loadPlatformResource(subjectName.utf8().data(), ResponseSampleRate);
79         ASSERT(concatenatedImpulseResponses);
80         if (!concatenatedImpulseResponses)
81             return 0;
82
83         bus = concatenatedImpulseResponses.release().leakRef();
84         audioBusMap.set(subjectName, bus);
85     } else
86         bus = iterator->value;
87
88     size_t responseLength = bus->length();
89     size_t expectedLength = static_cast<size_t>(TotalNumberOfResponses * ResponseFrameSize);
90
91     // Check number of channels and length. For now these are fixed and known.
92     bool isBusGood = responseLength == expectedLength && bus->numberOfChannels() == 2;
93     ASSERT(isBusGood);
94     if (!isBusGood)
95         return 0;
96
97     return bus;
98 }
99 #endif
100
101 // Takes advantage of the symmetry and creates a composite version of the two measured versions.  For example, we have both azimuth 30 and -30 degrees
102 // where the roles of left and right ears are reversed with respect to each other.
103 bool HRTFElevation::calculateSymmetricKernelsForAzimuthElevation(int azimuth, int elevation, float sampleRate, const String& subjectName,
104                                                                  RefPtr<HRTFKernel>& kernelL, RefPtr<HRTFKernel>& kernelR)
105 {
106     RefPtr<HRTFKernel> kernelL1;
107     RefPtr<HRTFKernel> kernelR1;
108     bool success = calculateKernelsForAzimuthElevation(azimuth, elevation, sampleRate, subjectName, kernelL1, kernelR1);
109     if (!success)
110         return false;
111         
112     // And symmetric version
113     int symmetricAzimuth = !azimuth ? 0 : 360 - azimuth;
114                                                               
115     RefPtr<HRTFKernel> kernelL2;
116     RefPtr<HRTFKernel> kernelR2;
117     success = calculateKernelsForAzimuthElevation(symmetricAzimuth, elevation, sampleRate, subjectName, kernelL2, kernelR2);
118     if (!success)
119         return false;
120         
121     // Notice L/R reversal in symmetric version.
122     kernelL = HRTFKernel::createInterpolatedKernel(kernelL1.get(), kernelR2.get(), 0.5f);
123     kernelR = HRTFKernel::createInterpolatedKernel(kernelR1.get(), kernelL2.get(), 0.5f);
124     
125     return true;
126 }
127
128 bool HRTFElevation::calculateKernelsForAzimuthElevation(int azimuth, int elevation, float sampleRate, const String& subjectName,
129                                                         RefPtr<HRTFKernel>& kernelL, RefPtr<HRTFKernel>& kernelR)
130 {
131     // Valid values for azimuth are 0 -> 345 in 15 degree increments.
132     // Valid values for elevation are -45 -> +90 in 15 degree increments.
133
134     bool isAzimuthGood = azimuth >= 0 && azimuth <= 345 && (azimuth / 15) * 15 == azimuth;
135     ASSERT(isAzimuthGood);
136     if (!isAzimuthGood)
137         return false;
138
139     bool isElevationGood = elevation >= -45 && elevation <= 90 && (elevation / 15) * 15 == elevation;
140     ASSERT(isElevationGood);
141     if (!isElevationGood)
142         return false;
143     
144     // Construct the resource name from the subject name, azimuth, and elevation, for example:
145     // "IRC_Composite_C_R0195_T015_P000"
146     // Note: the passed in subjectName is not a string passed in via JavaScript or the web.
147     // It's passed in as an internal ASCII identifier and is an implementation detail.
148     int positiveElevation = elevation < 0 ? elevation + 360 : elevation;
149
150 #ifdef USE_CONCATENATED_IMPULSE_RESPONSES
151     AudioBus* bus(getConcatenatedImpulseResponsesForSubject(subjectName));
152
153     if (!bus)
154         return false;
155
156     int elevationIndex = positiveElevation / AzimuthSpacing;
157     if (positiveElevation > 90)
158         elevationIndex -= AzimuthSpacing;
159
160     // The concatenated impulse response is a bus containing all
161     // the elevations per azimuth, for all azimuths by increasing
162     // order. So for a given azimuth and elevation we need to compute
163     // the index of the wanted audio frames in the concatenated table.
164     unsigned index = ((azimuth / AzimuthSpacing) * HRTFDatabase::NumberOfRawElevations) + elevationIndex;
165     bool isIndexGood = index < TotalNumberOfResponses;
166     ASSERT(isIndexGood);
167     if (!isIndexGood)
168         return false;
169
170     // Extract the individual impulse response from the concatenated
171     // responses and potentially sample-rate convert it to the desired
172     // (hardware) sample-rate.
173     unsigned startFrame = index * ResponseFrameSize;
174     unsigned stopFrame = startFrame + ResponseFrameSize;
175     RefPtr<AudioBus> preSampleRateConvertedResponse = AudioBus::createBufferFromRange(bus, startFrame, stopFrame);
176     RefPtr<AudioBus> response = AudioBus::createBySampleRateConverting(preSampleRateConvertedResponse.get(), false, sampleRate);
177     AudioChannel* leftEarImpulseResponse = response->channel(AudioBus::ChannelLeft);
178     AudioChannel* rightEarImpulseResponse = response->channel(AudioBus::ChannelRight);
179 #else
180     String resourceName = String::format("IRC_%s_C_R0195_T%03d_P%03d", subjectName.utf8().data(), azimuth, positiveElevation);
181
182     RefPtr<AudioBus> impulseResponse(AudioBus::loadPlatformResource(resourceName.utf8().data(), sampleRate));
183
184     ASSERT(impulseResponse.get());
185     if (!impulseResponse.get())
186         return false;
187     
188     size_t responseLength = impulseResponse->length();
189     size_t expectedLength = static_cast<size_t>(256 * (sampleRate / 44100.0));
190
191     // Check number of channels and length.  For now these are fixed and known.
192     bool isBusGood = responseLength == expectedLength && impulseResponse->numberOfChannels() == 2;
193     ASSERT(isBusGood);
194     if (!isBusGood)
195         return false;
196     
197     AudioChannel* leftEarImpulseResponse = impulseResponse->channelByType(AudioBus::ChannelLeft);
198     AudioChannel* rightEarImpulseResponse = impulseResponse->channelByType(AudioBus::ChannelRight);
199 #endif
200
201     // Note that depending on the fftSize returned by the panner, we may be truncating the impulse response we just loaded in.
202     const size_t fftSize = HRTFPanner::fftSizeForSampleRate(sampleRate);
203     kernelL = HRTFKernel::create(leftEarImpulseResponse, fftSize, sampleRate);
204     kernelR = HRTFKernel::create(rightEarImpulseResponse, fftSize, sampleRate);
205     
206     return true;
207 }
208
209 // The range of elevations for the IRCAM impulse responses varies depending on azimuth, but the minimum elevation appears to always be -45.
210 //
211 // Here's how it goes:
212 static int maxElevations[] = {
213         //  Azimuth
214         //
215     90, // 0  
216     45, // 15 
217     60, // 30 
218     45, // 45 
219     75, // 60 
220     45, // 75 
221     60, // 90 
222     45, // 105 
223     75, // 120 
224     45, // 135 
225     60, // 150 
226     45, // 165 
227     75, // 180 
228     45, // 195 
229     60, // 210 
230     45, // 225 
231     75, // 240 
232     45, // 255 
233     60, // 270 
234     45, // 285 
235     75, // 300 
236     45, // 315 
237     60, // 330 
238     45 //  345 
239 };
240
241 PassOwnPtr<HRTFElevation> HRTFElevation::createForSubject(const String& subjectName, int elevation, float sampleRate)
242 {
243     bool isElevationGood = elevation >= -45 && elevation <= 90 && (elevation / 15) * 15 == elevation;
244     ASSERT(isElevationGood);
245     if (!isElevationGood)
246         return nullptr;
247         
248     OwnPtr<HRTFKernelList> kernelListL = adoptPtr(new HRTFKernelList(NumberOfTotalAzimuths));
249     OwnPtr<HRTFKernelList> kernelListR = adoptPtr(new HRTFKernelList(NumberOfTotalAzimuths));
250
251     // Load convolution kernels from HRTF files.
252     int interpolatedIndex = 0;
253     for (unsigned rawIndex = 0; rawIndex < NumberOfRawAzimuths; ++rawIndex) {
254         // Don't let elevation exceed maximum for this azimuth.
255         int maxElevation = maxElevations[rawIndex];
256         int actualElevation = std::min(elevation, maxElevation);
257
258         bool success = calculateKernelsForAzimuthElevation(rawIndex * AzimuthSpacing, actualElevation, sampleRate, subjectName, kernelListL->at(interpolatedIndex), kernelListR->at(interpolatedIndex));
259         if (!success)
260             return nullptr;
261             
262         interpolatedIndex += InterpolationFactor;
263     }
264
265     // Now go back and interpolate intermediate azimuth values.
266     for (unsigned i = 0; i < NumberOfTotalAzimuths; i += InterpolationFactor) {
267         int j = (i + InterpolationFactor) % NumberOfTotalAzimuths;
268
269         // Create the interpolated convolution kernels and delays.
270         for (unsigned jj = 1; jj < InterpolationFactor; ++jj) {
271             float x = float(jj) / float(InterpolationFactor); // interpolate from 0 -> 1
272
273             (*kernelListL)[i + jj] = HRTFKernel::createInterpolatedKernel(kernelListL->at(i).get(), kernelListL->at(j).get(), x);
274             (*kernelListR)[i + jj] = HRTFKernel::createInterpolatedKernel(kernelListR->at(i).get(), kernelListR->at(j).get(), x);
275         }
276     }
277     
278     OwnPtr<HRTFElevation> hrtfElevation = adoptPtr(new HRTFElevation(kernelListL.release(), kernelListR.release(), elevation, sampleRate));
279     return hrtfElevation.release();
280 }
281
282 PassOwnPtr<HRTFElevation> HRTFElevation::createByInterpolatingSlices(HRTFElevation* hrtfElevation1, HRTFElevation* hrtfElevation2, float x, float sampleRate)
283 {
284     ASSERT(hrtfElevation1 && hrtfElevation2);
285     if (!hrtfElevation1 || !hrtfElevation2)
286         return nullptr;
287         
288     ASSERT(x >= 0.0 && x < 1.0);
289     
290     OwnPtr<HRTFKernelList> kernelListL = adoptPtr(new HRTFKernelList(NumberOfTotalAzimuths));
291     OwnPtr<HRTFKernelList> kernelListR = adoptPtr(new HRTFKernelList(NumberOfTotalAzimuths));
292
293     HRTFKernelList* kernelListL1 = hrtfElevation1->kernelListL();
294     HRTFKernelList* kernelListR1 = hrtfElevation1->kernelListR();
295     HRTFKernelList* kernelListL2 = hrtfElevation2->kernelListL();
296     HRTFKernelList* kernelListR2 = hrtfElevation2->kernelListR();
297     
298     // Interpolate kernels of corresponding azimuths of the two elevations.
299     for (unsigned i = 0; i < NumberOfTotalAzimuths; ++i) {
300         (*kernelListL)[i] = HRTFKernel::createInterpolatedKernel(kernelListL1->at(i).get(), kernelListL2->at(i).get(), x);
301         (*kernelListR)[i] = HRTFKernel::createInterpolatedKernel(kernelListR1->at(i).get(), kernelListR2->at(i).get(), x);
302     }
303
304     // Interpolate elevation angle.
305     double angle = (1.0 - x) * hrtfElevation1->elevationAngle() + x * hrtfElevation2->elevationAngle();
306     
307     OwnPtr<HRTFElevation> hrtfElevation = adoptPtr(new HRTFElevation(kernelListL.release(), kernelListR.release(), static_cast<int>(angle), sampleRate));
308     return hrtfElevation.release();  
309 }
310
311 void HRTFElevation::getKernelsFromAzimuth(double azimuthBlend, unsigned azimuthIndex, HRTFKernel* &kernelL, HRTFKernel* &kernelR, double& frameDelayL, double& frameDelayR)
312 {
313     bool checkAzimuthBlend = azimuthBlend >= 0.0 && azimuthBlend < 1.0;
314     ASSERT(checkAzimuthBlend);
315     if (!checkAzimuthBlend)
316         azimuthBlend = 0.0;
317     
318     unsigned numKernels = m_kernelListL->size();
319
320     bool isIndexGood = azimuthIndex < numKernels;
321     ASSERT(isIndexGood);
322     if (!isIndexGood) {
323         kernelL = 0;
324         kernelR = 0;
325         return;
326     }
327     
328     // Return the left and right kernels.
329     kernelL = m_kernelListL->at(azimuthIndex).get();
330     kernelR = m_kernelListR->at(azimuthIndex).get();
331
332     frameDelayL = m_kernelListL->at(azimuthIndex)->frameDelay();
333     frameDelayR = m_kernelListR->at(azimuthIndex)->frameDelay();
334
335     int azimuthIndex2 = (azimuthIndex + 1) % numKernels;
336     double frameDelay2L = m_kernelListL->at(azimuthIndex2)->frameDelay();
337     double frameDelay2R = m_kernelListR->at(azimuthIndex2)->frameDelay();
338
339     // Linearly interpolate delays.
340     frameDelayL = (1.0 - azimuthBlend) * frameDelayL + azimuthBlend * frameDelay2L;
341     frameDelayR = (1.0 - azimuthBlend) * frameDelayR + azimuthBlend * frameDelay2R;
342 }
343
344 } // namespace WebCore
345
346 #endif // ENABLE(WEB_AUDIO)