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_position(0, 0, 0) | |
53 , m_orientation(1, 0, 0) | |
54 , m_velocity(0, 0, 0) | |
55 , m_cachedPosition(0, 0, 0) | |
56 , m_cachedOrientation(1, 0, 0) | |
57 , m_cachedVelocity(0, 0, 0) | |
52 , m_lastGain(-1.0) | 58 , m_lastGain(-1.0) |
59 , m_cachedAzimuth(0) | |
60 , m_cachedElevation(0) | |
61 , m_cachedDistanceConeGain(0) | |
62 , m_cachedDopplerRate(1) | |
53 , m_connectionCount(0) | 63 , m_connectionCount(0) |
54 { | 64 { |
55 // Load the HRTF database asynchronously so we don't block the Javascript th read while creating the HRTF database. | 65 // Load the HRTF database asynchronously so we don't block the Javascript th read while creating the HRTF database. |
56 // The HRTF panner will return zeroes until the database is loaded. | 66 // The HRTF panner will return zeroes until the database is loaded. |
57 m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNece ssary(context->sampleRate()); | 67 m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNece ssary(context->sampleRate()); |
58 | 68 |
59 ScriptWrappable::init(this); | 69 ScriptWrappable::init(this); |
60 addInput(adoptPtr(new AudioNodeInput(this))); | 70 addInput(adoptPtr(new AudioNodeInput(this))); |
61 addOutput(adoptPtr(new AudioNodeOutput(this, 2))); | 71 addOutput(adoptPtr(new AudioNodeOutput(this, 2))); |
62 | 72 |
63 // Node-specific default mixing rules. | 73 // Node-specific default mixing rules. |
64 m_channelCount = 2; | 74 m_channelCount = 2; |
65 m_channelCountMode = ClampedMax; | 75 m_channelCountMode = ClampedMax; |
66 m_channelInterpretation = AudioBus::Speakers; | 76 m_channelInterpretation = AudioBus::Speakers; |
67 | 77 |
68 m_distanceGain = AudioParam::create(context, "distanceGain", 1.0, 0.0, 1.0); | 78 m_distanceGain = AudioParam::create(context, "distanceGain", 1.0, 0.0, 1.0); |
69 m_coneGain = AudioParam::create(context, "coneGain", 1.0, 0.0, 1.0); | 79 m_coneGain = AudioParam::create(context, "coneGain", 1.0, 0.0, 1.0); |
70 | 80 |
71 m_position = FloatPoint3D(0, 0, 0); | 81 m_cachedListener = AudioListener::create(); |
72 m_orientation = FloatPoint3D(1, 0, 0); | |
73 m_velocity = FloatPoint3D(0, 0, 0); | |
74 | 82 |
75 setNodeType(NodeTypePanner); | 83 setNodeType(NodeTypePanner); |
76 | 84 |
77 initialize(); | 85 initialize(); |
78 } | 86 } |
79 | 87 |
80 PannerNode::~PannerNode() | 88 PannerNode::~PannerNode() |
81 { | 89 { |
82 uninitialize(); | 90 uninitialize(); |
83 } | 91 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
124 return; | 132 return; |
125 } | 133 } |
126 } | 134 } |
127 | 135 |
128 // The audio thread can't block on this lock, so we call tryLock() instead. | 136 // The audio thread can't block on this lock, so we call tryLock() instead. |
129 MutexTryLocker tryLocker(m_pannerLock); | 137 MutexTryLocker tryLocker(m_pannerLock); |
130 if (tryLocker.locked()) { | 138 if (tryLocker.locked()) { |
131 // Apply the panning effect. | 139 // Apply the panning effect. |
132 double azimuth; | 140 double azimuth; |
133 double elevation; | 141 double elevation; |
134 getAzimuthElevation(&azimuth, &elevation); | 142 azimuthElevation(&azimuth, &elevation); |
143 | |
135 m_panner->pan(azimuth, elevation, source, destination, framesToProcess); | 144 m_panner->pan(azimuth, elevation, source, destination, framesToProcess); |
136 | 145 |
137 // Get the distance and cone gain. | 146 // Get the distance and cone gain. |
138 double totalGain = distanceConeGain(); | 147 float totalGain = distanceConeGain(); |
139 | 148 |
140 // Snap to desired gain at the beginning. | 149 // Snap to desired gain at the beginning. |
141 if (m_lastGain == -1.0) | 150 if (m_lastGain == -1.0) |
142 m_lastGain = totalGain; | 151 m_lastGain = totalGain; |
143 | 152 |
144 // Apply gain in-place with de-zippering. | 153 // Apply gain in-place with de-zippering. |
145 destination->copyWithGainFrom(*destination, &m_lastGain, totalGain); | 154 destination->copyWithGainFrom(*destination, &m_lastGain, totalGain); |
155 | |
156 // Update the cached listener in case listener has moved. | |
157 updateCachedListener(); | |
158 // Now update the cached source location in case the source has changed. | |
159 updateCachedSourceLocationInfo(); | |
146 } else { | 160 } else { |
147 // Too bad - The tryLock() failed. We must be in the middle of changing the panner. | 161 // Too bad - The tryLock() failed. We must be in the middle of changing the panner. |
148 destination->zero(); | 162 destination->zero(); |
149 } | 163 } |
150 } | 164 } |
151 | 165 |
152 void PannerNode::initialize() | 166 void PannerNode::initialize() |
153 { | 167 { |
154 if (isInitialized()) | 168 if (isInitialized()) |
155 return; | 169 return; |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
210 m_panningModel = model; | 224 m_panningModel = model; |
211 } | 225 } |
212 break; | 226 break; |
213 default: | 227 default: |
214 return false; | 228 return false; |
215 } | 229 } |
216 | 230 |
217 return true; | 231 return true; |
218 } | 232 } |
219 | 233 |
234 void PannerNode::setPosition(float x, float y, float z) | |
235 { | |
236 // FIXME : consider thread safety about m_position in audio thread. | |
237 // See http://crbugs.com/350583. | |
238 FloatPoint3D position = FloatPoint3D(x, y, z); | |
239 | |
240 if (m_position == position) | |
241 return; | |
242 | |
243 m_position = position; | |
244 } | |
245 | |
246 void PannerNode::setOrientation(float x, float y, float z) | |
247 { | |
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 | |
252 if (m_orientation == orientation) | |
253 return; | |
254 | |
255 m_orientation = orientation; | |
256 } | |
257 | |
258 void PannerNode::setVelocity(float x, float y, float z) | |
259 { | |
260 // FIXME : consider thread safety about m_velocity in audio thread. | |
261 // See http://crbugs.com/350583. | |
262 FloatPoint3D velocity = FloatPoint3D(x, y, z); | |
263 | |
264 if (m_velocity == velocity) | |
265 return; | |
266 | |
267 m_velocity = velocity; | |
268 } | |
269 | |
220 String PannerNode::distanceModel() const | 270 String PannerNode::distanceModel() const |
221 { | 271 { |
222 switch (const_cast<PannerNode*>(this)->m_distanceEffect.model()) { | 272 switch (const_cast<PannerNode*>(this)->m_distanceEffect.model()) { |
223 case DistanceEffect::ModelLinear: | 273 case DistanceEffect::ModelLinear: |
224 return "linear"; | 274 return "linear"; |
225 case DistanceEffect::ModelInverse: | 275 case DistanceEffect::ModelInverse: |
226 return "inverse"; | 276 return "inverse"; |
227 case DistanceEffect::ModelExponential: | 277 case DistanceEffect::ModelExponential: |
228 return "exponential"; | 278 return "exponential"; |
229 default: | 279 default: |
(...skipping 22 matching lines...) Expand all Loading... | |
252 case DistanceEffect::ModelExponential: | 302 case DistanceEffect::ModelExponential: |
253 m_distanceEffect.setModel(static_cast<DistanceEffect::ModelType>(model), true); | 303 m_distanceEffect.setModel(static_cast<DistanceEffect::ModelType>(model), true); |
254 break; | 304 break; |
255 default: | 305 default: |
256 return false; | 306 return false; |
257 } | 307 } |
258 | 308 |
259 return true; | 309 return true; |
260 } | 310 } |
261 | 311 |
262 void PannerNode::getAzimuthElevation(double* outAzimuth, double* outElevation) | 312 void PannerNode::calculateAzimuthElevation(double* outAzimuth, double* outElevat ion) |
263 { | 313 { |
264 // FIXME: we should cache azimuth and elevation (if possible), so we only re -calculate if a change has been made. | |
265 | |
266 double azimuth = 0.0; | 314 double azimuth = 0.0; |
267 | 315 |
268 // Calculate the source-listener vector | 316 // Calculate the source-listener vector |
269 FloatPoint3D listenerPosition = listener()->position(); | 317 FloatPoint3D listenerPosition = listener()->position(); |
270 FloatPoint3D sourceListener = m_position - listenerPosition; | 318 FloatPoint3D sourceListener = m_position - listenerPosition; |
271 | 319 |
272 if (sourceListener.isZero()) { | 320 if (sourceListener.isZero()) { |
273 // degenerate case if source and listener are at the same point | 321 // degenerate case if source and listener are at the same point |
274 *outAzimuth = 0.0; | 322 *outAzimuth = 0.0; |
275 *outElevation = 0.0; | 323 *outElevation = 0.0; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
316 elevation = 180.0 - elevation; | 364 elevation = 180.0 - elevation; |
317 else if (elevation < -90.0) | 365 else if (elevation < -90.0) |
318 elevation = -180.0 - elevation; | 366 elevation = -180.0 - elevation; |
319 | 367 |
320 if (outAzimuth) | 368 if (outAzimuth) |
321 *outAzimuth = azimuth; | 369 *outAzimuth = azimuth; |
322 if (outElevation) | 370 if (outElevation) |
323 *outElevation = elevation; | 371 *outElevation = elevation; |
324 } | 372 } |
325 | 373 |
326 float PannerNode::dopplerRate() | 374 |
375 double PannerNode::calculateDopplerRate() | |
327 { | 376 { |
328 double dopplerShift = 1.0; | 377 double dopplerShift = 1.0; |
329 | |
330 // FIXME: optimize for case when neither source nor listener has changed... | |
331 double dopplerFactor = listener()->dopplerFactor(); | 378 double dopplerFactor = listener()->dopplerFactor(); |
332 | 379 |
333 if (dopplerFactor > 0.0) { | 380 if (dopplerFactor > 0.0) { |
334 double speedOfSound = listener()->speedOfSound(); | 381 double speedOfSound = listener()->speedOfSound(); |
335 | 382 |
336 const FloatPoint3D &sourceVelocity = m_velocity; | 383 const FloatPoint3D &sourceVelocity = m_velocity; |
337 const FloatPoint3D &listenerVelocity = listener()->velocity(); | 384 const FloatPoint3D &listenerVelocity = listener()->velocity(); |
338 | 385 |
339 // Don't bother if both source and listener have no velocity | 386 // Don't bother if both source and listener have no velocity |
340 bool sourceHasVelocity = !sourceVelocity.isZero(); | 387 bool sourceHasVelocity = !sourceVelocity.isZero(); |
(...skipping 20 matching lines...) Expand all Loading... | |
361 fixNANs(dopplerShift); // avoid illegal values | 408 fixNANs(dopplerShift); // avoid illegal values |
362 | 409 |
363 // Limit the pitch shifting to 4 octaves up and 3 octaves down. | 410 // Limit the pitch shifting to 4 octaves up and 3 octaves down. |
364 if (dopplerShift > 16.0) | 411 if (dopplerShift > 16.0) |
365 dopplerShift = 16.0; | 412 dopplerShift = 16.0; |
366 else if (dopplerShift < 0.125) | 413 else if (dopplerShift < 0.125) |
367 dopplerShift = 0.125; | 414 dopplerShift = 0.125; |
368 } | 415 } |
369 } | 416 } |
370 | 417 |
371 return static_cast<float>(dopplerShift); | 418 return dopplerShift; |
372 } | 419 } |
373 | 420 |
374 float PannerNode::distanceConeGain() | 421 float PannerNode::calculateDistanceConeGain() |
375 { | 422 { |
376 FloatPoint3D listenerPosition = listener()->position(); | 423 FloatPoint3D listenerPosition = listener()->position(); |
377 | 424 |
378 double listenerDistance = m_position.distanceTo(listenerPosition); | 425 double listenerDistance = m_position.distanceTo(listenerPosition); |
379 double distanceGain = m_distanceEffect.gain(listenerDistance); | 426 double distanceGain = m_distanceEffect.gain(listenerDistance); |
380 | 427 |
381 m_distanceGain->setValue(static_cast<float>(distanceGain)); | 428 m_distanceGain->setValue(static_cast<float>(distanceGain)); |
382 | 429 |
383 // FIXME: could optimize by caching coneGain | |
384 double coneGain = m_coneEffect.gain(m_position, m_orientation, listenerPosit ion); | 430 double coneGain = m_coneEffect.gain(m_position, m_orientation, listenerPosit ion); |
385 | 431 |
386 m_coneGain->setValue(static_cast<float>(coneGain)); | 432 m_coneGain->setValue(static_cast<float>(coneGain)); |
387 | 433 |
388 return float(distanceGain * coneGain); | 434 return float(distanceGain * coneGain); |
389 } | 435 } |
390 | 436 |
437 void PannerNode::azimuthElevation(double* outAzimuth, double* outElevation) | |
438 { | |
439 if (isAzimuthElevationDirty()) | |
440 calculateAzimuthElevation(&m_cachedAzimuth, &m_cachedElevation); | |
441 | |
442 *outAzimuth = m_cachedAzimuth; | |
443 *outElevation = m_cachedElevation; | |
444 } | |
445 | |
446 double PannerNode::dopplerRate() | |
447 { | |
448 if (isDopplerRateDirty()) | |
449 m_cachedDopplerRate = calculateDopplerRate(); | |
450 | |
451 return m_cachedDopplerRate; | |
452 } | |
453 | |
454 float PannerNode::distanceConeGain() | |
455 { | |
456 if (isDistanceConeGainDirty()) | |
457 m_cachedDistanceConeGain = calculateDistanceConeGain(); | |
458 | |
459 return m_cachedDistanceConeGain; | |
460 } | |
461 | |
462 bool PannerNode::isAzimuthElevationDirty() | |
463 { | |
464 if (m_cachedPosition != m_position) | |
Raymond Toy
2014/03/10 20:47:20
Nit: Add comment:
// Do a quick test and return
KhNo
2014/03/11 05:32:12
Done.
| |
465 return true; | |
466 | |
467 if (m_cachedListener->position() != listener()->position() | |
468 || m_cachedListener->orientation() != listener()->orientation() | |
469 || m_cachedListener->upVector() != listener()->upVector()) | |
470 return true; | |
471 | |
472 return false; | |
473 } | |
474 | |
475 bool PannerNode::isDistanceConeGainDirty() | |
476 { | |
477 if (m_cachedPosition != m_position || m_cachedOrientation != m_orientation) | |
478 return true; | |
479 | |
480 if (m_cachedListener->position() != listener()->position()) | |
481 return true; | |
482 | |
483 return false; | |
484 } | |
485 | |
486 bool PannerNode::isDopplerRateDirty() | |
487 { | |
488 if (m_cachedPosition != m_position || m_cachedVelocity != m_velocity) | |
489 return true; | |
490 | |
491 if (m_cachedListener->position() != listener()->position() | |
492 || m_cachedListener->velocity() != listener()->velocity() | |
493 || m_cachedListener->dopplerFactor() != listener()->dopplerFactor() | |
494 || m_cachedListener->speedOfSound() != listener()->speedOfSound()) | |
495 return true; | |
496 | |
497 return false; | |
498 } | |
499 | |
391 void PannerNode::notifyAudioSourcesConnectedToNode(AudioNode* node, HashMap<Audi oNode*, bool>& visitedNodes) | 500 void PannerNode::notifyAudioSourcesConnectedToNode(AudioNode* node, HashMap<Audi oNode*, bool>& visitedNodes) |
392 { | 501 { |
393 ASSERT(node); | 502 ASSERT(node); |
394 if (!node) | 503 if (!node) |
395 return; | 504 return; |
396 | 505 |
397 // First check if this node is an AudioBufferSourceNode. If so, let it know about us so that doppler shift pitch can be taken into account. | 506 // First check if this node is an AudioBufferSourceNode. If so, let it know about us so that doppler shift pitch can be taken into account. |
398 if (node->nodeType() == NodeTypeAudioBufferSource) { | 507 if (node->nodeType() == NodeTypeAudioBufferSource) { |
399 AudioBufferSourceNode* bufferSourceNode = static_cast<AudioBufferSourceN ode*>(node); | 508 AudioBufferSourceNode* bufferSourceNode = static_cast<AudioBufferSourceN ode*>(node); |
400 bufferSourceNode->setPannerNode(this); | 509 bufferSourceNode->setPannerNode(this); |
(...skipping 12 matching lines...) Expand all Loading... | |
413 // mark it as visited and recurse through the node looking for s ources. | 522 // mark it as visited and recurse through the node looking for s ources. |
414 if (iterator == visitedNodes.end()) { | 523 if (iterator == visitedNodes.end()) { |
415 visitedNodes.set(connectedNode, true); | 524 visitedNodes.set(connectedNode, true); |
416 notifyAudioSourcesConnectedToNode(connectedNode, visitedNode s); // recurse | 525 notifyAudioSourcesConnectedToNode(connectedNode, visitedNode s); // recurse |
417 } | 526 } |
418 } | 527 } |
419 } | 528 } |
420 } | 529 } |
421 } | 530 } |
422 | 531 |
532 void PannerNode::updateCachedListener() | |
533 { | |
534 m_cachedListener->setPosition(listener()->position()); | |
535 m_cachedListener->setOrientation(listener()->orientation()); | |
536 m_cachedListener->setUpVector(listener()->upVector()); | |
537 m_cachedListener->setVelocity(listener()->velocity()); | |
538 m_cachedListener->setDopplerFactor(listener()->dopplerFactor()); | |
539 m_cachedListener->setSpeedOfSound(listener()->speedOfSound()); | |
540 } | |
541 | |
542 void PannerNode::updateCachedSourceLocationInfo() | |
543 { | |
544 m_cachedPosition = m_position; | |
545 m_cachedOrientation = m_orientation; | |
546 m_cachedVelocity = m_velocity; | |
547 } | |
548 | |
423 } // namespace WebCore | 549 } // namespace WebCore |
424 | 550 |
425 #endif // ENABLE(WEB_AUDIO) | 551 #endif // ENABLE(WEB_AUDIO) |
OLD | NEW |