| 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 |