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 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
42 | 42 |
43 static void fixNANs(double &x) | 43 static void fixNANs(double &x) |
44 { | 44 { |
45 if (std::isnan(x) || std::isinf(x)) | 45 if (std::isnan(x) || std::isinf(x)) |
46 x = 0.0; | 46 x = 0.0; |
47 } | 47 } |
48 | 48 |
49 PannerNode::PannerNode(AudioContext* context, float sampleRate) | 49 PannerNode::PannerNode(AudioContext* context, float sampleRate) |
50 : AudioNode(context, sampleRate) | 50 : AudioNode(context, sampleRate) |
51 , m_panningModel(Panner::PanningModelHRTF) | 51 , m_panningModel(Panner::PanningModelHRTF) |
| 52 , m_distanceModel(DistanceEffect::ModelInverse) |
52 , m_position(0, 0, 0) | 53 , m_position(0, 0, 0) |
53 , m_orientation(1, 0, 0) | 54 , m_orientation(1, 0, 0) |
54 , m_velocity(0, 0, 0) | 55 , m_velocity(0, 0, 0) |
55 , m_cachedPosition(0, 0, 0) | 56 , m_cachedPosition(0, 0, 0) |
56 , m_cachedOrientation(1, 0, 0) | 57 , m_cachedOrientation(1, 0, 0) |
57 , m_cachedVelocity(0, 0, 0) | 58 , m_cachedVelocity(0, 0, 0) |
58 , m_lastGain(-1.0) | 59 , m_lastGain(-1.0) |
59 , m_cachedAzimuth(0) | 60 , m_cachedAzimuth(0) |
60 , m_cachedElevation(0) | 61 , m_cachedElevation(0) |
61 , m_cachedDistanceConeGain(0) | 62 , m_cachedDistanceConeGain(1.0f) |
62 , m_cachedDopplerRate(1) | 63 , m_cachedDopplerRate(1) |
63 , m_connectionCount(0) | 64 , m_connectionCount(0) |
64 { | 65 { |
65 // Load the HRTF database asynchronously so we don't block the Javascript th
read while creating the HRTF database. | 66 // Load the HRTF database asynchronously so we don't block the Javascript th
read while creating the HRTF database. |
66 // The HRTF panner will return zeroes until the database is loaded. | 67 // The HRTF panner will return zeroes until the database is loaded. |
67 m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNece
ssary(context->sampleRate()); | 68 m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNece
ssary(context->sampleRate()); |
68 | 69 |
69 ScriptWrappable::init(this); | 70 ScriptWrappable::init(this); |
70 addInput(adoptPtr(new AudioNodeInput(this))); | 71 addInput(adoptPtr(new AudioNodeInput(this))); |
71 addOutput(adoptPtr(new AudioNodeOutput(this, 2))); | 72 addOutput(adoptPtr(new AudioNodeOutput(this, 2))); |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
116 destination->zero(); | 117 destination->zero(); |
117 return; | 118 return; |
118 } | 119 } |
119 | 120 |
120 AudioBus* source = input(0)->bus(); | 121 AudioBus* source = input(0)->bus(); |
121 if (!source) { | 122 if (!source) { |
122 destination->zero(); | 123 destination->zero(); |
123 return; | 124 return; |
124 } | 125 } |
125 | 126 |
126 // HRTFDatabase should be loaded before proceeding for offline audio context
when panningModel() is "HRTF". | 127 // The audio thread can't block on this lock, so we call tryLock() instead. |
127 if (panningModel() == "HRTF" && !m_hrtfDatabaseLoader->isLoaded()) { | 128 MutexTryLocker tryLocker(m_processLock); |
128 if (context()->isOfflineContext()) { | 129 if (tryLocker.locked()) { |
129 m_hrtfDatabaseLoader->waitForLoaderThreadCompletion(); | 130 // HRTFDatabase should be loaded before proceeding for offline audio con
text when panningModel() is "HRTF". |
130 } else { | 131 if (panningModel() == "HRTF" && !m_hrtfDatabaseLoader->isLoaded()) { |
131 destination->zero(); | 132 if (context()->isOfflineContext()) { |
132 return; | 133 m_hrtfDatabaseLoader->waitForLoaderThreadCompletion(); |
| 134 } else { |
| 135 destination->zero(); |
| 136 return; |
| 137 } |
133 } | 138 } |
134 } | |
135 | 139 |
136 // The audio thread can't block on this lock, so we call tryLock() instead. | |
137 MutexTryLocker tryLocker(m_pannerLock); | |
138 if (tryLocker.locked()) { | |
139 // Apply the panning effect. | 140 // Apply the panning effect. |
140 double azimuth; | 141 double azimuth; |
141 double elevation; | 142 double elevation; |
142 azimuthElevation(&azimuth, &elevation); | 143 azimuthElevation(&azimuth, &elevation); |
143 | 144 |
144 m_panner->pan(azimuth, elevation, source, destination, framesToProcess); | 145 m_panner->pan(azimuth, elevation, source, destination, framesToProcess); |
145 | 146 |
146 // Get the distance and cone gain. | 147 // Get the distance and cone gain. |
147 float totalGain = distanceConeGain(); | 148 float totalGain = distanceConeGain(); |
148 | 149 |
149 // Snap to desired gain at the beginning. | 150 // Snap to desired gain at the beginning. |
150 if (m_lastGain == -1.0) | 151 if (m_lastGain == -1.0) |
151 m_lastGain = totalGain; | 152 m_lastGain = totalGain; |
152 | 153 |
153 // Apply gain in-place with de-zippering. | 154 // Apply gain in-place with de-zippering. |
154 destination->copyWithGainFrom(*destination, &m_lastGain, totalGain); | 155 destination->copyWithGainFrom(*destination, &m_lastGain, totalGain); |
155 | 156 |
156 // Update the cached listener in case listener has moved. | 157 // Update the cached listener in case listener has moved. |
157 updateCachedListener(); | 158 updateCachedListener(); |
158 // Now update the cached source location in case the source has changed. | 159 // Now update the cached source location in case the source has changed. |
159 updateCachedSourceLocationInfo(); | 160 updateCachedSourceLocationInfo(); |
160 } else { | 161 } else { |
161 // Too bad - The tryLock() failed. We must be in the middle of changing
the panner. | 162 // Too bad - The tryLock() failed. |
| 163 // We must be in the middle of changing the panning model, the distance
model, or the source's location information. |
162 destination->zero(); | 164 destination->zero(); |
163 } | 165 } |
164 } | 166 } |
165 | 167 |
166 void PannerNode::initialize() | 168 void PannerNode::initialize() |
167 { | 169 { |
168 if (isInitialized()) | 170 if (isInitialized()) |
169 return; | 171 return; |
170 | 172 |
171 m_panner = Panner::create(m_panningModel, sampleRate(), m_hrtfDatabaseLoader
.get()); | 173 m_panner = Panner::create(m_panningModel, sampleRate(), m_hrtfDatabaseLoader
.get()); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
210 ASSERT_NOT_REACHED(); | 212 ASSERT_NOT_REACHED(); |
211 } | 213 } |
212 | 214 |
213 bool PannerNode::setPanningModel(unsigned model) | 215 bool PannerNode::setPanningModel(unsigned model) |
214 { | 216 { |
215 switch (model) { | 217 switch (model) { |
216 case EQUALPOWER: | 218 case EQUALPOWER: |
217 case HRTF: | 219 case HRTF: |
218 if (!m_panner.get() || model != m_panningModel) { | 220 if (!m_panner.get() || model != m_panningModel) { |
219 // This synchronizes with process(). | 221 // This synchronizes with process(). |
220 MutexLocker processLocker(m_pannerLock); | 222 MutexLocker processLocker(m_processLock); |
221 | 223 |
222 OwnPtr<Panner> newPanner = Panner::create(model, sampleRate(), m_hrt
fDatabaseLoader.get()); | 224 OwnPtr<Panner> newPanner = Panner::create(model, sampleRate(), m_hrt
fDatabaseLoader.get()); |
223 m_panner = newPanner.release(); | 225 m_panner = newPanner.release(); |
224 m_panningModel = model; | 226 m_panningModel = model; |
225 } | 227 } |
226 break; | 228 break; |
227 default: | 229 default: |
228 return false; | 230 return false; |
229 } | 231 } |
230 | 232 |
231 return true; | 233 return true; |
232 } | 234 } |
233 | 235 |
234 void PannerNode::setPosition(float x, float y, float z) | 236 void PannerNode::setPosition(float x, float y, float z) |
235 { | 237 { |
236 // FIXME : consider thread safety about m_position in audio thread. | |
237 // See http://crbugs.com/350583. | |
238 FloatPoint3D position = FloatPoint3D(x, y, z); | 238 FloatPoint3D position = FloatPoint3D(x, y, z); |
239 | 239 |
240 if (m_position == position) | 240 if (m_position == position) |
241 return; | 241 return; |
242 | 242 |
| 243 // This synchronizes with process(). |
| 244 MutexLocker processLocker(m_processLock); |
| 245 |
243 m_position = position; | 246 m_position = position; |
244 } | 247 } |
245 | 248 |
246 void PannerNode::setOrientation(float x, float y, float z) | 249 void PannerNode::setOrientation(float x, float y, float z) |
247 { | 250 { |
248 // FIXME : consider thread safety about m_orientation in audio thread. | |
249 // See http://crbugs.com/350583. | |
250 FloatPoint3D orientation = FloatPoint3D(x, y, z); | 251 FloatPoint3D orientation = FloatPoint3D(x, y, z); |
251 | 252 |
252 if (m_orientation == orientation) | 253 if (m_orientation == orientation) |
253 return; | 254 return; |
254 | 255 |
| 256 // This synchronizes with process(). |
| 257 MutexLocker processLocker(m_processLock); |
| 258 |
255 m_orientation = orientation; | 259 m_orientation = orientation; |
256 } | 260 } |
257 | 261 |
258 void PannerNode::setVelocity(float x, float y, float z) | 262 void PannerNode::setVelocity(float x, float y, float z) |
259 { | 263 { |
260 // FIXME : consider thread safety about m_velocity in audio thread. | |
261 // See http://crbugs.com/350583. | |
262 FloatPoint3D velocity = FloatPoint3D(x, y, z); | 264 FloatPoint3D velocity = FloatPoint3D(x, y, z); |
263 | 265 |
264 if (m_velocity == velocity) | 266 if (m_velocity == velocity) |
265 return; | 267 return; |
266 | 268 |
| 269 // This synchronizes with process(). |
| 270 MutexLocker processLocker(m_processLock); |
| 271 |
267 m_velocity = velocity; | 272 m_velocity = velocity; |
268 } | 273 } |
269 | 274 |
270 String PannerNode::distanceModel() const | 275 String PannerNode::distanceModel() const |
271 { | 276 { |
272 switch (const_cast<PannerNode*>(this)->m_distanceEffect.model()) { | 277 switch (const_cast<PannerNode*>(this)->m_distanceEffect.model()) { |
273 case DistanceEffect::ModelLinear: | 278 case DistanceEffect::ModelLinear: |
274 return "linear"; | 279 return "linear"; |
275 case DistanceEffect::ModelInverse: | 280 case DistanceEffect::ModelInverse: |
276 return "inverse"; | 281 return "inverse"; |
(...skipping 16 matching lines...) Expand all Loading... |
293 else | 298 else |
294 ASSERT_NOT_REACHED(); | 299 ASSERT_NOT_REACHED(); |
295 } | 300 } |
296 | 301 |
297 bool PannerNode::setDistanceModel(unsigned model) | 302 bool PannerNode::setDistanceModel(unsigned model) |
298 { | 303 { |
299 switch (model) { | 304 switch (model) { |
300 case DistanceEffect::ModelLinear: | 305 case DistanceEffect::ModelLinear: |
301 case DistanceEffect::ModelInverse: | 306 case DistanceEffect::ModelInverse: |
302 case DistanceEffect::ModelExponential: | 307 case DistanceEffect::ModelExponential: |
303 m_distanceEffect.setModel(static_cast<DistanceEffect::ModelType>(model),
true); | 308 if (model != m_distanceModel) { |
| 309 // This synchronizes with process(). |
| 310 MutexLocker processLocker(m_processLock); |
| 311 m_distanceEffect.setModel(static_cast<DistanceEffect::ModelType>(mod
el), true); |
| 312 m_distanceModel = model; |
| 313 } |
304 break; | 314 break; |
305 default: | 315 default: |
306 return false; | 316 return false; |
307 } | 317 } |
308 | 318 |
309 return true; | 319 return true; |
310 } | 320 } |
311 | 321 |
312 void PannerNode::calculateAzimuthElevation(double* outAzimuth, double* outElevat
ion) | 322 void PannerNode::calculateAzimuthElevation(double* outAzimuth, double* outElevat
ion) |
313 { | 323 { |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
364 elevation = 180.0 - elevation; | 374 elevation = 180.0 - elevation; |
365 else if (elevation < -90.0) | 375 else if (elevation < -90.0) |
366 elevation = -180.0 - elevation; | 376 elevation = -180.0 - elevation; |
367 | 377 |
368 if (outAzimuth) | 378 if (outAzimuth) |
369 *outAzimuth = azimuth; | 379 *outAzimuth = azimuth; |
370 if (outElevation) | 380 if (outElevation) |
371 *outElevation = elevation; | 381 *outElevation = elevation; |
372 } | 382 } |
373 | 383 |
374 | |
375 double PannerNode::calculateDopplerRate() | 384 double PannerNode::calculateDopplerRate() |
376 { | 385 { |
377 double dopplerShift = 1.0; | 386 double dopplerShift = 1.0; |
378 double dopplerFactor = listener()->dopplerFactor(); | 387 double dopplerFactor = listener()->dopplerFactor(); |
379 | 388 |
380 if (dopplerFactor > 0.0) { | 389 if (dopplerFactor > 0.0) { |
381 double speedOfSound = listener()->speedOfSound(); | 390 double speedOfSound = listener()->speedOfSound(); |
382 | 391 |
383 const FloatPoint3D &sourceVelocity = m_velocity; | 392 const FloatPoint3D &sourceVelocity = m_velocity; |
384 const FloatPoint3D &listenerVelocity = listener()->velocity(); | 393 const FloatPoint3D &listenerVelocity = listener()->velocity(); |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
429 | 438 |
430 double coneGain = m_coneEffect.gain(m_position, m_orientation, listenerPosit
ion); | 439 double coneGain = m_coneEffect.gain(m_position, m_orientation, listenerPosit
ion); |
431 | 440 |
432 m_coneGain->setValue(static_cast<float>(coneGain)); | 441 m_coneGain->setValue(static_cast<float>(coneGain)); |
433 | 442 |
434 return float(distanceGain * coneGain); | 443 return float(distanceGain * coneGain); |
435 } | 444 } |
436 | 445 |
437 void PannerNode::azimuthElevation(double* outAzimuth, double* outElevation) | 446 void PannerNode::azimuthElevation(double* outAzimuth, double* outElevation) |
438 { | 447 { |
| 448 ASSERT(context()->isAudioThread()); |
| 449 |
439 if (isAzimuthElevationDirty()) | 450 if (isAzimuthElevationDirty()) |
440 calculateAzimuthElevation(&m_cachedAzimuth, &m_cachedElevation); | 451 calculateAzimuthElevation(&m_cachedAzimuth, &m_cachedElevation); |
441 | 452 |
442 *outAzimuth = m_cachedAzimuth; | 453 *outAzimuth = m_cachedAzimuth; |
443 *outElevation = m_cachedElevation; | 454 *outElevation = m_cachedElevation; |
444 } | 455 } |
445 | 456 |
446 double PannerNode::dopplerRate() | 457 double PannerNode::dopplerRate() |
447 { | 458 { |
| 459 ASSERT(context()->isAudioThread()); |
| 460 |
448 if (isDopplerRateDirty()) | 461 if (isDopplerRateDirty()) |
449 m_cachedDopplerRate = calculateDopplerRate(); | 462 m_cachedDopplerRate = calculateDopplerRate(); |
450 | 463 |
451 return m_cachedDopplerRate; | 464 return m_cachedDopplerRate; |
452 } | 465 } |
453 | 466 |
454 float PannerNode::distanceConeGain() | 467 float PannerNode::distanceConeGain() |
455 { | 468 { |
| 469 ASSERT(context()->isAudioThread()); |
| 470 |
456 if (isDistanceConeGainDirty()) | 471 if (isDistanceConeGainDirty()) |
457 m_cachedDistanceConeGain = calculateDistanceConeGain(); | 472 m_cachedDistanceConeGain = calculateDistanceConeGain(); |
458 | 473 |
459 return m_cachedDistanceConeGain; | 474 return m_cachedDistanceConeGain; |
460 } | 475 } |
461 | 476 |
462 bool PannerNode::isAzimuthElevationDirty() | 477 bool PannerNode::isAzimuthElevationDirty() |
463 { | 478 { |
464 // Do a quick test and return if possible. | 479 // Do a quick test and return if possible. |
465 if (m_cachedPosition != m_position) | 480 if (m_cachedPosition != m_position) |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
527 visitedNodes.set(connectedNode, true); | 542 visitedNodes.set(connectedNode, true); |
528 notifyAudioSourcesConnectedToNode(connectedNode, visitedNode
s); // recurse | 543 notifyAudioSourcesConnectedToNode(connectedNode, visitedNode
s); // recurse |
529 } | 544 } |
530 } | 545 } |
531 } | 546 } |
532 } | 547 } |
533 } | 548 } |
534 | 549 |
535 void PannerNode::updateCachedListener() | 550 void PannerNode::updateCachedListener() |
536 { | 551 { |
| 552 ASSERT(context()->isAudioThread()); |
| 553 |
537 m_cachedListener->setPosition(listener()->position()); | 554 m_cachedListener->setPosition(listener()->position()); |
538 m_cachedListener->setOrientation(listener()->orientation()); | 555 m_cachedListener->setOrientation(listener()->orientation()); |
539 m_cachedListener->setUpVector(listener()->upVector()); | 556 m_cachedListener->setUpVector(listener()->upVector()); |
540 m_cachedListener->setVelocity(listener()->velocity()); | 557 m_cachedListener->setVelocity(listener()->velocity()); |
541 m_cachedListener->setDopplerFactor(listener()->dopplerFactor()); | 558 m_cachedListener->setDopplerFactor(listener()->dopplerFactor()); |
542 m_cachedListener->setSpeedOfSound(listener()->speedOfSound()); | 559 m_cachedListener->setSpeedOfSound(listener()->speedOfSound()); |
543 } | 560 } |
544 | 561 |
545 void PannerNode::updateCachedSourceLocationInfo() | 562 void PannerNode::updateCachedSourceLocationInfo() |
546 { | 563 { |
| 564 ASSERT(context()->isAudioThread()); |
| 565 |
547 m_cachedPosition = m_position; | 566 m_cachedPosition = m_position; |
548 m_cachedOrientation = m_orientation; | 567 m_cachedOrientation = m_orientation; |
549 m_cachedVelocity = m_velocity; | 568 m_cachedVelocity = m_velocity; |
550 } | 569 } |
551 | 570 |
552 } // namespace WebCore | 571 } // namespace WebCore |
553 | 572 |
554 #endif // ENABLE(WEB_AUDIO) | 573 #endif // ENABLE(WEB_AUDIO) |
OLD | NEW |