Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(140)

Side by Side Diff: Source/modules/webaudio/PannerNode.cpp

Issue 232453003: Add logic to cache elements of panner node from calculation with own attributes and AudioListener (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Fix some comments and function names Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
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_distanceModel(DistanceEffect::ModelInverse)
53 , m_position(0, 0, 0) 53 , m_position(0, 0, 0)
54 , m_orientation(1, 0, 0) 54 , m_orientation(1, 0, 0)
55 , m_velocity(0, 0, 0) 55 , m_velocity(0, 0, 0)
56 , m_cachedPosition(0, 0, 0) 56 , m_isAzimuthElevationDirty(true)
57 , m_cachedOrientation(1, 0, 0) 57 , m_isDistanceConeGainDirty(true)
58 , m_cachedVelocity(0, 0, 0) 58 , m_isDopplerRateDirty(true)
59 , m_lastGain(-1.0) 59 , m_lastGain(-1.0)
60 , m_cachedAzimuth(0) 60 , m_cachedAzimuth(0)
61 , m_cachedElevation(0) 61 , m_cachedElevation(0)
62 , m_cachedDistanceConeGain(1.0f) 62 , m_cachedDistanceConeGain(1.0f)
63 , m_cachedDopplerRate(1) 63 , m_cachedDopplerRate(1)
64 , m_connectionCount(0) 64 , m_connectionCount(0)
65 { 65 {
66 // 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.
67 // The HRTF panner will return zeroes until the database is loaded. 67 // The HRTF panner will return zeroes until the database is loaded.
68 m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNece ssary(context->sampleRate()); 68 m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNece ssary(context->sampleRate());
69 69
70 ScriptWrappable::init(this); 70 ScriptWrappable::init(this);
71 addInput(adoptPtr(new AudioNodeInput(this))); 71 addInput(adoptPtr(new AudioNodeInput(this)));
72 addOutput(adoptPtr(new AudioNodeOutput(this, 2))); 72 addOutput(adoptPtr(new AudioNodeOutput(this, 2)));
73 73
74 // Node-specific default mixing rules. 74 // Node-specific default mixing rules.
75 m_channelCount = 2; 75 m_channelCount = 2;
76 m_channelCountMode = ClampedMax; 76 m_channelCountMode = ClampedMax;
77 m_channelInterpretation = AudioBus::Speakers; 77 m_channelInterpretation = AudioBus::Speakers;
78 78
79 m_cachedListener = AudioListener::create();
80
81 setNodeType(NodeTypePanner); 79 setNodeType(NodeTypePanner);
82 80
83 initialize(); 81 initialize();
84 } 82 }
85 83
86 PannerNode::~PannerNode() 84 PannerNode::~PannerNode()
87 { 85 {
88 uninitialize(); 86 uninitialize();
89 } 87 }
90 88
(...skipping 25 matching lines...) Expand all
116 } 114 }
117 115
118 AudioBus* source = input(0)->bus(); 116 AudioBus* source = input(0)->bus();
119 if (!source) { 117 if (!source) {
120 destination->zero(); 118 destination->zero();
121 return; 119 return;
122 } 120 }
123 121
124 // The audio thread can't block on this lock, so we call tryLock() instead. 122 // The audio thread can't block on this lock, so we call tryLock() instead.
125 MutexTryLocker tryLocker(m_processLock); 123 MutexTryLocker tryLocker(m_processLock);
126 if (tryLocker.locked()) { 124 MutexTryLocker tryListenerLocker(listener()->listenerLock());
125
126 if (tryLocker.locked() && tryListenerLocker.locked()) {
127 // HRTFDatabase should be loaded before proceeding for offline audio con text when the panning model is HRTF. 127 // HRTFDatabase should be loaded before proceeding for offline audio con text when the panning model is HRTF.
128 if (m_panningModel == HRTF && !m_hrtfDatabaseLoader->isLoaded()) { 128 if (m_panningModel == HRTF && !m_hrtfDatabaseLoader->isLoaded()) {
129 if (context()->isOfflineContext()) { 129 if (context()->isOfflineContext()) {
130 m_hrtfDatabaseLoader->waitForLoaderThreadCompletion(); 130 m_hrtfDatabaseLoader->waitForLoaderThreadCompletion();
131 } else { 131 } else {
132 destination->zero(); 132 destination->zero();
133 return; 133 return;
134 } 134 }
135 } 135 }
136 136
137 // Apply the panning effect. 137 // Apply the panning effect.
138 double azimuth; 138 double azimuth;
139 double elevation; 139 double elevation;
140 azimuthElevation(&azimuth, &elevation); 140 azimuthElevation(&azimuth, &elevation);
141 141
142 m_panner->pan(azimuth, elevation, source, destination, framesToProcess); 142 m_panner->pan(azimuth, elevation, source, destination, framesToProcess);
143 143
144 // Get the distance and cone gain. 144 // Get the distance and cone gain.
145 float totalGain = distanceConeGain(); 145 float totalGain = distanceConeGain();
146 146
147 // Snap to desired gain at the beginning. 147 // Snap to desired gain at the beginning.
148 if (m_lastGain == -1.0) 148 if (m_lastGain == -1.0)
149 m_lastGain = totalGain; 149 m_lastGain = totalGain;
150 150
151 // Apply gain in-place with de-zippering. 151 // Apply gain in-place with de-zippering.
152 destination->copyWithGainFrom(*destination, &m_lastGain, totalGain); 152 destination->copyWithGainFrom(*destination, &m_lastGain, totalGain);
153
154 // Update the cached listener in case listener has moved.
155 updateCachedListener();
156 // Now update the cached source location in case the source has changed.
157 updateCachedSourceLocationInfo();
158 } else { 153 } else {
159 // Too bad - The tryLock() failed. 154 // Too bad - The tryLock() failed.
160 // We must be in the middle of changing the panning model, the distance model, or the source's location information. 155 // We must be in the middle of changing the properties of the panner or the listener.
161 destination->zero(); 156 destination->zero();
162 } 157 }
163 } 158 }
164 159
165 void PannerNode::initialize() 160 void PannerNode::initialize()
166 { 161 {
167 if (isInitialized()) 162 if (isInitialized())
168 return; 163 return;
169 164
170 m_panner = Panner::create(m_panningModel, sampleRate(), m_hrtfDatabaseLoader .get()); 165 m_panner = Panner::create(m_panningModel, sampleRate(), m_hrtfDatabaseLoader .get());
166 listener()->addPanner(this);
171 167
172 AudioNode::initialize(); 168 AudioNode::initialize();
173 } 169 }
174 170
175 void PannerNode::uninitialize() 171 void PannerNode::uninitialize()
176 { 172 {
177 if (!isInitialized()) 173 if (!isInitialized())
178 return; 174 return;
179 175
180 m_panner.clear(); 176 m_panner.clear();
177 listener()->removePanner(this);
178
181 AudioNode::uninitialize(); 179 AudioNode::uninitialize();
182 } 180 }
183 181
184 AudioListener* PannerNode::listener() 182 AudioListener* PannerNode::listener()
185 { 183 {
186 return context()->listener(); 184 return context()->listener();
187 } 185 }
188 186
189 String PannerNode::panningModel() const 187 String PannerNode::panningModel() const
190 { 188 {
191 switch (m_panningModel) { 189 switch (m_panningModel) {
192 case EQUALPOWER: 190 case EqualPower:
193 return "equalpower"; 191 return "equalpower";
194 case HRTF: 192 case HRTF:
195 return "HRTF"; 193 return "HRTF";
196 default: 194 default:
197 ASSERT_NOT_REACHED(); 195 ASSERT_NOT_REACHED();
198 return "HRTF"; 196 return "HRTF";
199 } 197 }
200 } 198 }
201 199
202 void PannerNode::setPanningModel(const String& model) 200 void PannerNode::setPanningModel(const String& model)
203 { 201 {
204 if (model == "equalpower") 202 if (model == "equalpower")
205 setPanningModel(EQUALPOWER); 203 setPanningModel(EqualPower);
206 else if (model == "HRTF") 204 else if (model == "HRTF")
207 setPanningModel(HRTF); 205 setPanningModel(HRTF);
208 } 206 }
209 207
210 bool PannerNode::setPanningModel(unsigned model) 208 bool PannerNode::setPanningModel(unsigned model)
211 { 209 {
212 switch (model) { 210 switch (model) {
213 case EQUALPOWER: 211 case EqualPower:
214 case HRTF: 212 case HRTF:
215 if (!m_panner.get() || model != m_panningModel) { 213 if (!m_panner.get() || model != m_panningModel) {
216 // This synchronizes with process(). 214 // This synchronizes with process().
217 MutexLocker processLocker(m_processLock); 215 MutexLocker processLocker(m_processLock);
218
Raymond Toy 2014/04/21 16:48:26 Question: If you didn't delete this line (and you
KhNo 2014/04/21 17:29:01 That was for making code consensus before calling
Raymond Toy 2014/04/22 00:57:49 I leave it up to you. It's just that I don't like
KhNo 2014/04/22 06:20:09 Definitely, you're right. It is good for others. I
219 OwnPtr<Panner> newPanner = Panner::create(model, sampleRate(), m_hrt fDatabaseLoader.get()); 216 OwnPtr<Panner> newPanner = Panner::create(model, sampleRate(), m_hrt fDatabaseLoader.get());
220 m_panner = newPanner.release(); 217 m_panner = newPanner.release();
221 m_panningModel = model; 218 m_panningModel = model;
222 } 219 }
223 break; 220 break;
224 default: 221 default:
225 return false; 222 return false;
226 } 223 }
227 224
228 return true; 225 return true;
229 } 226 }
230 227
231 void PannerNode::setPosition(float x, float y, float z)
232 {
233 FloatPoint3D position = FloatPoint3D(x, y, z);
234
235 if (m_position == position)
236 return;
237
238 // This synchronizes with process().
239 MutexLocker processLocker(m_processLock);
240
241 m_position = position;
242 }
243
244 void PannerNode::setOrientation(float x, float y, float z)
245 {
246 FloatPoint3D orientation = FloatPoint3D(x, y, z);
247
248 if (m_orientation == orientation)
249 return;
250
251 // This synchronizes with process().
252 MutexLocker processLocker(m_processLock);
253
254 m_orientation = orientation;
255 }
256
257 void PannerNode::setVelocity(float x, float y, float z)
258 {
259 FloatPoint3D velocity = FloatPoint3D(x, y, z);
260
261 if (m_velocity == velocity)
262 return;
263
264 // This synchronizes with process().
265 MutexLocker processLocker(m_processLock);
266
267 m_velocity = velocity;
268 }
269
270 String PannerNode::distanceModel() const 228 String PannerNode::distanceModel() const
271 { 229 {
272 switch (const_cast<PannerNode*>(this)->m_distanceEffect.model()) { 230 switch (const_cast<PannerNode*>(this)->m_distanceEffect.model()) {
273 case DistanceEffect::ModelLinear: 231 case DistanceEffect::ModelLinear:
274 return "linear"; 232 return "linear";
275 case DistanceEffect::ModelInverse: 233 case DistanceEffect::ModelInverse:
276 return "inverse"; 234 return "inverse";
277 case DistanceEffect::ModelExponential: 235 case DistanceEffect::ModelExponential:
278 return "exponential"; 236 return "exponential";
279 default: 237 default:
(...skipping 25 matching lines...) Expand all
305 m_distanceModel = model; 263 m_distanceModel = model;
306 } 264 }
307 break; 265 break;
308 default: 266 default:
309 return false; 267 return false;
310 } 268 }
311 269
312 return true; 270 return true;
313 } 271 }
314 272
273 void PannerNode::setRefDistance(double distance)
274 {
275 if (refDistance() == distance)
276 return;
277
278 // This synchronizes with process().
279 MutexLocker processLocker(m_processLock);
280 m_distanceEffect.setRefDistance(distance);
281 markPannerAsDirty(PannerNode::DistanceConeGainDirty);
282 }
283
284 void PannerNode::setMaxDistance(double distance)
285 {
286 if (maxDistance() == distance)
287 return;
288
289 // This synchronizes with process().
290 MutexLocker processLocker(m_processLock);
291 m_distanceEffect.setMaxDistance(distance);
292 markPannerAsDirty(PannerNode::DistanceConeGainDirty);
293 }
294
295 void PannerNode::setRolloffFactor(double factor)
296 {
297 if (rolloffFactor() == factor)
298 return;
299
300 // This synchronizes with process().
301 MutexLocker processLocker(m_processLock);
302 m_distanceEffect.setRolloffFactor(factor);
303 markPannerAsDirty(PannerNode::DistanceConeGainDirty);
304 }
305
306 void PannerNode::setConeInnerAngle(double angle)
307 {
308 if (coneInnerAngle() == angle)
309 return;
310
311 // This synchronizes with process().
312 MutexLocker processLocker(m_processLock);
313 m_coneEffect.setInnerAngle(angle);
314 markPannerAsDirty(PannerNode::DistanceConeGainDirty);
315 }
316
317 void PannerNode::setConeOuterAngle(double angle)
318 {
319 if (coneOuterAngle() == angle)
320 return;
321
322 // This synchronizes with process().
323 MutexLocker processLocker(m_processLock);
324 m_coneEffect.setOuterAngle(angle);
325 markPannerAsDirty(PannerNode::DistanceConeGainDirty);
326 }
327
328 void PannerNode::setConeOuterGain(double angle)
329 {
330 if (coneOuterGain() == angle)
331 return;
332
333 // This synchronizes with process().
334 MutexLocker processLocker(m_processLock);
335 m_coneEffect.setOuterGain(angle);
336 markPannerAsDirty(PannerNode::DistanceConeGainDirty);
337 }
338
339 void PannerNode::setPosition(float x, float y, float z)
340 {
341 FloatPoint3D position = FloatPoint3D(x, y, z);
342
343 if (m_position == position)
344 return;
345
346 // This synchronizes with process().
347 MutexLocker processLocker(m_processLock);
348 m_position = position;
349 markPannerAsDirty(PannerNode::AzimuthElevationDirty | PannerNode::DistanceCo neGainDirty | PannerNode::DopplerRateDirty);
350 }
351
352 void PannerNode::setOrientation(float x, float y, float z)
353 {
354 FloatPoint3D orientation = FloatPoint3D(x, y, z);
355
356 if (m_orientation == orientation)
357 return;
358
359 // This synchronizes with process().
360 MutexLocker processLocker(m_processLock);
361 m_orientation = orientation;
362 markPannerAsDirty(PannerNode::DistanceConeGainDirty);
363 }
364
365 void PannerNode::setVelocity(float x, float y, float z)
366 {
367 FloatPoint3D velocity = FloatPoint3D(x, y, z);
368
369 if (m_velocity == velocity)
370 return;
371
372 // This synchronizes with process().
373 MutexLocker processLocker(m_processLock);
374 m_velocity = velocity;
375 markPannerAsDirty(PannerNode::DopplerRateDirty);
376 }
377
315 void PannerNode::calculateAzimuthElevation(double* outAzimuth, double* outElevat ion) 378 void PannerNode::calculateAzimuthElevation(double* outAzimuth, double* outElevat ion)
316 { 379 {
317 double azimuth = 0.0; 380 double azimuth = 0.0;
318 381
319 // Calculate the source-listener vector 382 // Calculate the source-listener vector
320 FloatPoint3D listenerPosition = listener()->position(); 383 FloatPoint3D listenerPosition = listener()->position();
321 FloatPoint3D sourceListener = m_position - listenerPosition; 384 FloatPoint3D sourceListener = m_position - listenerPosition;
322 385
323 if (sourceListener.isZero()) { 386 if (sourceListener.isZero()) {
324 // degenerate case if source and listener are at the same point 387 // degenerate case if source and listener are at the same point
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
428 double distanceGain = m_distanceEffect.gain(listenerDistance); 491 double distanceGain = m_distanceEffect.gain(listenerDistance);
429 double coneGain = m_coneEffect.gain(m_position, m_orientation, listenerPosit ion); 492 double coneGain = m_coneEffect.gain(m_position, m_orientation, listenerPosit ion);
430 493
431 return float(distanceGain * coneGain); 494 return float(distanceGain * coneGain);
432 } 495 }
433 496
434 void PannerNode::azimuthElevation(double* outAzimuth, double* outElevation) 497 void PannerNode::azimuthElevation(double* outAzimuth, double* outElevation)
435 { 498 {
436 ASSERT(context()->isAudioThread()); 499 ASSERT(context()->isAudioThread());
437 500
438 if (isAzimuthElevationDirty()) 501 if (isAzimuthElevationDirty()) {
439 calculateAzimuthElevation(&m_cachedAzimuth, &m_cachedElevation); 502 calculateAzimuthElevation(&m_cachedAzimuth, &m_cachedElevation);
503 m_isAzimuthElevationDirty = false;
504 }
440 505
441 *outAzimuth = m_cachedAzimuth; 506 *outAzimuth = m_cachedAzimuth;
442 *outElevation = m_cachedElevation; 507 *outElevation = m_cachedElevation;
443 } 508 }
444 509
445 double PannerNode::dopplerRate() 510 double PannerNode::dopplerRate()
446 { 511 {
447 ASSERT(context()->isAudioThread()); 512 ASSERT(context()->isAudioThread());
448 513
449 if (isDopplerRateDirty()) 514 if (isDopplerRateDirty()) {
450 m_cachedDopplerRate = calculateDopplerRate(); 515 m_cachedDopplerRate = calculateDopplerRate();
516 m_isDopplerRateDirty = false;
517 }
451 518
452 return m_cachedDopplerRate; 519 return m_cachedDopplerRate;
453 } 520 }
454 521
455 float PannerNode::distanceConeGain() 522 float PannerNode::distanceConeGain()
456 { 523 {
457 ASSERT(context()->isAudioThread()); 524 ASSERT(context()->isAudioThread());
458 525
459 if (isDistanceConeGainDirty()) 526 if (isDistanceConeGainDirty()) {
460 m_cachedDistanceConeGain = calculateDistanceConeGain(); 527 m_cachedDistanceConeGain = calculateDistanceConeGain();
528 m_isDistanceConeGainDirty = false;
529 }
461 530
462 return m_cachedDistanceConeGain; 531 return m_cachedDistanceConeGain;
463 } 532 }
464 533
465 bool PannerNode::isAzimuthElevationDirty() 534 void PannerNode::markPannerAsDirty(unsigned dirty)
466 { 535 {
467 // Do a quick test and return if possible. 536 if (dirty & PannerNode::AzimuthElevationDirty)
468 if (m_cachedPosition != m_position) 537 m_isAzimuthElevationDirty = true;
469 return true;
470 538
471 if (m_cachedListener->position() != listener()->position() 539 if (dirty & PannerNode::DistanceConeGainDirty)
472 || m_cachedListener->orientation() != listener()->orientation() 540 m_isDistanceConeGainDirty = true;
473 || m_cachedListener->upVector() != listener()->upVector())
474 return true;
475 541
476 return false; 542 if (dirty & PannerNode::DopplerRateDirty)
477 } 543 m_isDopplerRateDirty = true;
478
479 bool PannerNode::isDistanceConeGainDirty()
480 {
481 // Do a quick test and return if possible.
482 if (m_cachedPosition != m_position || m_cachedOrientation != m_orientation)
483 return true;
484
485 if (m_cachedListener->position() != listener()->position())
486 return true;
487
488 return false;
489 }
490
491 bool PannerNode::isDopplerRateDirty()
492 {
493 // Do a quick test and return if possible.
494 if (m_cachedPosition != m_position || m_cachedVelocity != m_velocity)
495 return true;
496
497 if (m_cachedListener->position() != listener()->position()
498 || m_cachedListener->velocity() != listener()->velocity()
499 || m_cachedListener->dopplerFactor() != listener()->dopplerFactor()
500 || m_cachedListener->speedOfSound() != listener()->speedOfSound())
501 return true;
502
503 return false;
504 } 544 }
505 545
506 void PannerNode::notifyAudioSourcesConnectedToNode(AudioNode* node, HashMap<Audi oNode*, bool>& visitedNodes) 546 void PannerNode::notifyAudioSourcesConnectedToNode(AudioNode* node, HashMap<Audi oNode*, bool>& visitedNodes)
507 { 547 {
508 ASSERT(node); 548 ASSERT(node);
509 if (!node) 549 if (!node)
510 return; 550 return;
511 551
512 // 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. 552 // 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.
513 if (node->nodeType() == NodeTypeAudioBufferSource) { 553 if (node->nodeType() == NodeTypeAudioBufferSource) {
(...skipping 14 matching lines...) Expand all
528 // mark it as visited and recurse through the node looking for s ources. 568 // mark it as visited and recurse through the node looking for s ources.
529 if (iterator == visitedNodes.end()) { 569 if (iterator == visitedNodes.end()) {
530 visitedNodes.set(connectedNode, true); 570 visitedNodes.set(connectedNode, true);
531 notifyAudioSourcesConnectedToNode(connectedNode, visitedNode s); // recurse 571 notifyAudioSourcesConnectedToNode(connectedNode, visitedNode s); // recurse
532 } 572 }
533 } 573 }
534 } 574 }
535 } 575 }
536 } 576 }
537 577
538 void PannerNode::updateCachedListener()
539 {
540 ASSERT(context()->isAudioThread());
541
542 m_cachedListener->setPosition(listener()->position());
543 m_cachedListener->setOrientation(listener()->orientation());
544 m_cachedListener->setUpVector(listener()->upVector());
545 m_cachedListener->setVelocity(listener()->velocity());
546 m_cachedListener->setDopplerFactor(listener()->dopplerFactor());
547 m_cachedListener->setSpeedOfSound(listener()->speedOfSound());
548 }
549
550 void PannerNode::updateCachedSourceLocationInfo()
551 {
552 ASSERT(context()->isAudioThread());
553
554 m_cachedPosition = m_position;
555 m_cachedOrientation = m_orientation;
556 m_cachedVelocity = m_velocity;
557 }
558
559 } // namespace WebCore 578 } // namespace WebCore
560 579
561 #endif // ENABLE(WEB_AUDIO) 580 #endif // ENABLE(WEB_AUDIO)
OLDNEW
« Source/modules/webaudio/PannerNode.h ('K') | « Source/modules/webaudio/PannerNode.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698