OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2010 Google Inc. All rights reserved. | 2 * Copyright (C) 2010 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
55 // Total number of components of an HRTF database. | 55 // Total number of components of an HRTF database. |
56 const size_t TotalNumberOfResponses = 240; | 56 const size_t TotalNumberOfResponses = 240; |
57 | 57 |
58 // Number of frames in an individual impulse response. | 58 // Number of frames in an individual impulse response. |
59 const size_t ResponseFrameSize = 256; | 59 const size_t ResponseFrameSize = 256; |
60 | 60 |
61 // Sample-rate of the spatialization impulse responses as stored in the resource
file. | 61 // Sample-rate of the spatialization impulse responses as stored in the resource
file. |
62 // The impulse responses may be resampled to a different sample-rate (depending
on the audio hardware) when they are loaded. | 62 // The impulse responses may be resampled to a different sample-rate (depending
on the audio hardware) when they are loaded. |
63 const float ResponseSampleRate = 44100; | 63 const float ResponseSampleRate = 44100; |
64 | 64 |
65 #if USE(WEBAUDIO_GSTREAMER) | |
66 #define USE_CONCATENATED_IMPULSE_RESPONSES | |
67 #endif | |
68 | |
69 #ifdef USE_CONCATENATED_IMPULSE_RESPONSES | |
70 // Lazily load a concatenated HRTF database for given subject and store it in a | |
71 // local hash table to ensure quick efficient future retrievals. | |
72 static AudioBus* getConcatenatedImpulseResponsesForSubject(const String& subject
Name) | |
73 { | |
74 typedef HashMap<String, AudioBus*> AudioBusMap; | |
75 DEFINE_STATIC_LOCAL(AudioBusMap, audioBusMap, ()); | |
76 | |
77 AudioBus* bus; | |
78 AudioBusMap::iterator iterator = audioBusMap.find(subjectName); | |
79 if (iterator == audioBusMap.end()) { | |
80 OwnPtr<AudioBus> concatenatedImpulseResponses = AudioBus::loadPlatformRe
source(subjectName.utf8().data(), ResponseSampleRate); | |
81 ASSERT(concatenatedImpulseResponses); | |
82 if (!concatenatedImpulseResponses) | |
83 return 0; | |
84 | |
85 bus = concatenatedImpulseResponses.leakPtr(); | |
86 audioBusMap.set(subjectName, bus); | |
87 } else | |
88 bus = iterator->value; | |
89 | |
90 size_t responseLength = bus->length(); | |
91 size_t expectedLength = static_cast<size_t>(TotalNumberOfResponses * Respons
eFrameSize); | |
92 | |
93 // Check number of channels and length. For now these are fixed and known. | |
94 bool isBusGood = responseLength == expectedLength && bus->numberOfChannels()
== 2; | |
95 ASSERT(isBusGood); | |
96 if (!isBusGood) | |
97 return 0; | |
98 | |
99 return bus; | |
100 } | |
101 #endif | |
102 | |
103 // Takes advantage of the symmetry and creates a composite version of the two me
asured versions. For example, we have both azimuth 30 and -30 degrees | 65 // Takes advantage of the symmetry and creates a composite version of the two me
asured versions. For example, we have both azimuth 30 and -30 degrees |
104 // where the roles of left and right ears are reversed with respect to each othe
r. | 66 // where the roles of left and right ears are reversed with respect to each othe
r. |
105 bool HRTFElevation::calculateSymmetricKernelsForAzimuthElevation(int azimuth, in
t elevation, float sampleRate, const String& subjectName, | 67 bool HRTFElevation::calculateSymmetricKernelsForAzimuthElevation(int azimuth, in
t elevation, float sampleRate, const String& subjectName, |
106 RefPtr<HRTFKern
el>& kernelL, RefPtr<HRTFKernel>& kernelR) | 68 RefPtr<HRTFKern
el>& kernelL, RefPtr<HRTFKernel>& kernelR) |
107 { | 69 { |
108 RefPtr<HRTFKernel> kernelL1; | 70 RefPtr<HRTFKernel> kernelL1; |
109 RefPtr<HRTFKernel> kernelR1; | 71 RefPtr<HRTFKernel> kernelR1; |
110 bool success = calculateKernelsForAzimuthElevation(azimuth, elevation, sampl
eRate, subjectName, kernelL1, kernelR1); | 72 bool success = calculateKernelsForAzimuthElevation(azimuth, elevation, sampl
eRate, subjectName, kernelL1, kernelR1); |
111 if (!success) | 73 if (!success) |
112 return false; | 74 return false; |
(...skipping 29 matching lines...) Expand all Loading... |
142 ASSERT(isElevationGood); | 104 ASSERT(isElevationGood); |
143 if (!isElevationGood) | 105 if (!isElevationGood) |
144 return false; | 106 return false; |
145 | 107 |
146 // Construct the resource name from the subject name, azimuth, and elevation
, for example: | 108 // Construct the resource name from the subject name, azimuth, and elevation
, for example: |
147 // "IRC_Composite_C_R0195_T015_P000" | 109 // "IRC_Composite_C_R0195_T015_P000" |
148 // Note: the passed in subjectName is not a string passed in via JavaScript
or the web. | 110 // Note: the passed in subjectName is not a string passed in via JavaScript
or the web. |
149 // It's passed in as an internal ASCII identifier and is an implementation d
etail. | 111 // It's passed in as an internal ASCII identifier and is an implementation d
etail. |
150 int positiveElevation = elevation < 0 ? elevation + 360 : elevation; | 112 int positiveElevation = elevation < 0 ? elevation + 360 : elevation; |
151 | 113 |
152 #ifdef USE_CONCATENATED_IMPULSE_RESPONSES | |
153 AudioBus* bus(getConcatenatedImpulseResponsesForSubject(subjectName)); | |
154 | |
155 if (!bus) | |
156 return false; | |
157 | |
158 int elevationIndex = positiveElevation / AzimuthSpacing; | |
159 if (positiveElevation > 90) | |
160 elevationIndex -= AzimuthSpacing; | |
161 | |
162 // The concatenated impulse response is a bus containing all | |
163 // the elevations per azimuth, for all azimuths by increasing | |
164 // order. So for a given azimuth and elevation we need to compute | |
165 // the index of the wanted audio frames in the concatenated table. | |
166 unsigned index = ((azimuth / AzimuthSpacing) * HRTFDatabase::NumberOfRawElev
ations) + elevationIndex; | |
167 bool isIndexGood = index < TotalNumberOfResponses; | |
168 ASSERT(isIndexGood); | |
169 if (!isIndexGood) | |
170 return false; | |
171 | |
172 // Extract the individual impulse response from the concatenated | |
173 // responses and potentially sample-rate convert it to the desired | |
174 // (hardware) sample-rate. | |
175 unsigned startFrame = index * ResponseFrameSize; | |
176 unsigned stopFrame = startFrame + ResponseFrameSize; | |
177 OwnPtr<AudioBus> preSampleRateConvertedResponse = AudioBus::createBufferFrom
Range(bus, startFrame, stopFrame); | |
178 OwnPtr<AudioBus> response = AudioBus::createBySampleRateConverting(preSample
RateConvertedResponse.get(), false, sampleRate); | |
179 AudioChannel* leftEarImpulseResponse = response->channel(AudioBus::ChannelLe
ft); | |
180 AudioChannel* rightEarImpulseResponse = response->channel(AudioBus::ChannelR
ight); | |
181 #else | |
182 String resourceName = String::format("IRC_%s_C_R0195_T%03d_P%03d", subjectNa
me.utf8().data(), azimuth, positiveElevation); | 114 String resourceName = String::format("IRC_%s_C_R0195_T%03d_P%03d", subjectNa
me.utf8().data(), azimuth, positiveElevation); |
183 | 115 |
184 OwnPtr<AudioBus> impulseResponse(AudioBus::loadPlatformResource(resourceName
.utf8().data(), sampleRate)); | 116 OwnPtr<AudioBus> impulseResponse(AudioBus::loadPlatformResource(resourceName
.utf8().data(), sampleRate)); |
185 | 117 |
186 ASSERT(impulseResponse.get()); | 118 ASSERT(impulseResponse.get()); |
187 if (!impulseResponse.get()) | 119 if (!impulseResponse.get()) |
188 return false; | 120 return false; |
189 | 121 |
190 size_t responseLength = impulseResponse->length(); | 122 size_t responseLength = impulseResponse->length(); |
191 size_t expectedLength = static_cast<size_t>(256 * (sampleRate / 44100.0)); | 123 size_t expectedLength = static_cast<size_t>(256 * (sampleRate / 44100.0)); |
192 | 124 |
193 // Check number of channels and length. For now these are fixed and known. | 125 // Check number of channels and length. For now these are fixed and known. |
194 bool isBusGood = responseLength == expectedLength && impulseResponse->number
OfChannels() == 2; | 126 bool isBusGood = responseLength == expectedLength && impulseResponse->number
OfChannels() == 2; |
195 ASSERT(isBusGood); | 127 ASSERT(isBusGood); |
196 if (!isBusGood) | 128 if (!isBusGood) |
197 return false; | 129 return false; |
198 | 130 |
199 AudioChannel* leftEarImpulseResponse = impulseResponse->channelByType(AudioB
us::ChannelLeft); | 131 AudioChannel* leftEarImpulseResponse = impulseResponse->channelByType(AudioB
us::ChannelLeft); |
200 AudioChannel* rightEarImpulseResponse = impulseResponse->channelByType(Audio
Bus::ChannelRight); | 132 AudioChannel* rightEarImpulseResponse = impulseResponse->channelByType(Audio
Bus::ChannelRight); |
201 #endif | |
202 | 133 |
203 // Note that depending on the fftSize returned by the panner, we may be trun
cating the impulse response we just loaded in. | 134 // Note that depending on the fftSize returned by the panner, we may be trun
cating the impulse response we just loaded in. |
204 const size_t fftSize = HRTFPanner::fftSizeForSampleRate(sampleRate); | 135 const size_t fftSize = HRTFPanner::fftSizeForSampleRate(sampleRate); |
205 kernelL = HRTFKernel::create(leftEarImpulseResponse, fftSize, sampleRate); | 136 kernelL = HRTFKernel::create(leftEarImpulseResponse, fftSize, sampleRate); |
206 kernelR = HRTFKernel::create(rightEarImpulseResponse, fftSize, sampleRate); | 137 kernelR = HRTFKernel::create(rightEarImpulseResponse, fftSize, sampleRate); |
207 | 138 |
208 return true; | 139 return true; |
209 } | 140 } |
210 | 141 |
211 // The range of elevations for the IRCAM impulse responses varies depending on a
zimuth, but the minimum elevation appears to always be -45. | 142 // The range of elevations for the IRCAM impulse responses varies depending on a
zimuth, but the minimum elevation appears to always be -45. |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
346 void HRTFElevation::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const | 277 void HRTFElevation::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
347 { | 278 { |
348 MemoryClassInfo info(memoryObjectInfo, this, PlatformMemoryTypes::AudioShare
dData); | 279 MemoryClassInfo info(memoryObjectInfo, this, PlatformMemoryTypes::AudioShare
dData); |
349 info.addMember(m_kernelListL, "kernelListL"); | 280 info.addMember(m_kernelListL, "kernelListL"); |
350 info.addMember(m_kernelListR, "kernelListR"); | 281 info.addMember(m_kernelListR, "kernelListR"); |
351 } | 282 } |
352 | 283 |
353 } // namespace WebCore | 284 } // namespace WebCore |
354 | 285 |
355 #endif // ENABLE(WEB_AUDIO) | 286 #endif // ENABLE(WEB_AUDIO) |
OLD | NEW |