| 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 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 130 m_deferredTaskHandler(DeferredTaskHandler::create()), | 130 m_deferredTaskHandler(DeferredTaskHandler::create()), |
| 131 m_contextState(Suspended), | 131 m_contextState(Suspended), |
| 132 m_closedContextSampleRate(-1), | 132 m_closedContextSampleRate(-1), |
| 133 m_periodicWaveSine(nullptr), | 133 m_periodicWaveSine(nullptr), |
| 134 m_periodicWaveSquare(nullptr), | 134 m_periodicWaveSquare(nullptr), |
| 135 m_periodicWaveSawtooth(nullptr), | 135 m_periodicWaveSawtooth(nullptr), |
| 136 m_periodicWaveTriangle(nullptr) {} | 136 m_periodicWaveTriangle(nullptr) {} |
| 137 | 137 |
| 138 BaseAudioContext::~BaseAudioContext() { | 138 BaseAudioContext::~BaseAudioContext() { |
| 139 deferredTaskHandler().contextWillBeDestroyed(); | 139 deferredTaskHandler().contextWillBeDestroyed(); |
| 140 // AudioNodes keep a reference to their context, so there should be no way to
be in the destructor if there are still AudioNodes around. | 140 // AudioNodes keep a reference to their context, so there should be no way to |
| 141 // be in the destructor if there are still AudioNodes around. |
| 141 DCHECK(!isDestinationInitialized()); | 142 DCHECK(!isDestinationInitialized()); |
| 142 DCHECK(!m_activeSourceNodes.size()); | 143 DCHECK(!m_activeSourceNodes.size()); |
| 143 DCHECK(!m_finishedSourceHandlers.size()); | 144 DCHECK(!m_finishedSourceHandlers.size()); |
| 144 DCHECK(!m_isResolvingResumePromises); | 145 DCHECK(!m_isResolvingResumePromises); |
| 145 DCHECK(!m_resumeResolvers.size()); | 146 DCHECK(!m_resumeResolvers.size()); |
| 146 } | 147 } |
| 147 | 148 |
| 148 void BaseAudioContext::initialize() { | 149 void BaseAudioContext::initialize() { |
| 149 if (isDestinationInitialized()) | 150 if (isDestinationInitialized()) |
| 150 return; | 151 return; |
| 151 | 152 |
| 152 FFTFrame::initialize(); | 153 FFTFrame::initialize(); |
| 153 | 154 |
| 154 if (m_destinationNode) { | 155 if (m_destinationNode) { |
| 155 m_destinationNode->handler().initialize(); | 156 m_destinationNode->handler().initialize(); |
| 156 // The AudioParams in the listener need access to the destination node, so o
nly create the | 157 // The AudioParams in the listener need access to the destination node, so |
| 157 // listener if the destination node exists. | 158 // only create the listener if the destination node exists. |
| 158 m_listener = AudioListener::create(*this); | 159 m_listener = AudioListener::create(*this); |
| 159 } | 160 } |
| 160 } | 161 } |
| 161 | 162 |
| 162 void BaseAudioContext::clear() { | 163 void BaseAudioContext::clear() { |
| 163 m_destinationNode.clear(); | 164 m_destinationNode.clear(); |
| 164 // The audio rendering thread is dead. Nobody will schedule AudioHandler | 165 // The audio rendering thread is dead. Nobody will schedule AudioHandler |
| 165 // deletion. Let's do it ourselves. | 166 // deletion. Let's do it ourselves. |
| 166 deferredTaskHandler().clearHandlersToBeDeleted(); | 167 deferredTaskHandler().clearHandlersToBeDeleted(); |
| 167 m_isCleared = true; | 168 m_isCleared = true; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 193 void BaseAudioContext::stop() { | 194 void BaseAudioContext::stop() { |
| 194 uninitialize(); | 195 uninitialize(); |
| 195 } | 196 } |
| 196 | 197 |
| 197 bool BaseAudioContext::hasPendingActivity() const { | 198 bool BaseAudioContext::hasPendingActivity() const { |
| 198 // There's no pending activity if the audio context has been cleared. | 199 // There's no pending activity if the audio context has been cleared. |
| 199 return !m_isCleared; | 200 return !m_isCleared; |
| 200 } | 201 } |
| 201 | 202 |
| 202 AudioDestinationNode* BaseAudioContext::destination() const { | 203 AudioDestinationNode* BaseAudioContext::destination() const { |
| 203 // Cannot be called from the audio thread because this method touches objects
managed by Oilpan, | 204 // Cannot be called from the audio thread because this method touches objects |
| 204 // and the audio thread is not managed by Oilpan. | 205 // managed by Oilpan, and the audio thread is not managed by Oilpan. |
| 205 DCHECK(!isAudioThread()); | 206 DCHECK(!isAudioThread()); |
| 206 return m_destinationNode; | 207 return m_destinationNode; |
| 207 } | 208 } |
| 208 | 209 |
| 209 void BaseAudioContext::throwExceptionForClosedState( | 210 void BaseAudioContext::throwExceptionForClosedState( |
| 210 ExceptionState& exceptionState) { | 211 ExceptionState& exceptionState) { |
| 211 exceptionState.throwDOMException(InvalidStateError, | 212 exceptionState.throwDOMException(InvalidStateError, |
| 212 "AudioContext has been closed."); | 213 "AudioContext has been closed."); |
| 213 } | 214 } |
| 214 | 215 |
| 215 AudioBuffer* BaseAudioContext::createBuffer(unsigned numberOfChannels, | 216 AudioBuffer* BaseAudioContext::createBuffer(unsigned numberOfChannels, |
| 216 size_t numberOfFrames, | 217 size_t numberOfFrames, |
| 217 float sampleRate, | 218 float sampleRate, |
| 218 ExceptionState& exceptionState) { | 219 ExceptionState& exceptionState) { |
| 219 // It's ok to call createBuffer, even if the context is closed because the Aud
ioBuffer doesn't | 220 // It's ok to call createBuffer, even if the context is closed because the |
| 220 // really "belong" to any particular context. | 221 // AudioBuffer doesn't really "belong" to any particular context. |
| 221 | 222 |
| 222 AudioBuffer* buffer = AudioBuffer::create(numberOfChannels, numberOfFrames, | 223 AudioBuffer* buffer = AudioBuffer::create(numberOfChannels, numberOfFrames, |
| 223 sampleRate, exceptionState); | 224 sampleRate, exceptionState); |
| 224 | 225 |
| 225 if (buffer) { | 226 if (buffer) { |
| 226 // Only record the data if the creation succeeded. | 227 // Only record the data if the creation succeeded. |
| 227 DEFINE_STATIC_LOCAL(SparseHistogram, audioBufferChannelsHistogram, | 228 DEFINE_STATIC_LOCAL(SparseHistogram, audioBufferChannelsHistogram, |
| 228 ("WebAudio.AudioBuffer.NumberOfChannels")); | 229 ("WebAudio.AudioBuffer.NumberOfChannels")); |
| 229 | 230 |
| 230 // Arbitrarly limit the maximum length to 1 million frames (about 20 sec | 231 // Arbitrarly limit the maximum length to 1 million frames (about 20 sec |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 313 m_decodeAudioResolvers.remove(resolver); | 314 m_decodeAudioResolvers.remove(resolver); |
| 314 } | 315 } |
| 315 | 316 |
| 316 AudioBufferSourceNode* BaseAudioContext::createBufferSource( | 317 AudioBufferSourceNode* BaseAudioContext::createBufferSource( |
| 317 ExceptionState& exceptionState) { | 318 ExceptionState& exceptionState) { |
| 318 DCHECK(isMainThread()); | 319 DCHECK(isMainThread()); |
| 319 | 320 |
| 320 AudioBufferSourceNode* node = | 321 AudioBufferSourceNode* node = |
| 321 AudioBufferSourceNode::create(*this, exceptionState); | 322 AudioBufferSourceNode::create(*this, exceptionState); |
| 322 | 323 |
| 323 // Do not add a reference to this source node now. The reference will be added
when start() is | 324 // Do not add a reference to this source node now. The reference will be added |
| 324 // called. | 325 // when start() is called. |
| 325 | 326 |
| 326 return node; | 327 return node; |
| 327 } | 328 } |
| 328 | 329 |
| 329 MediaElementAudioSourceNode* BaseAudioContext::createMediaElementSource( | 330 MediaElementAudioSourceNode* BaseAudioContext::createMediaElementSource( |
| 330 HTMLMediaElement* mediaElement, | 331 HTMLMediaElement* mediaElement, |
| 331 ExceptionState& exceptionState) { | 332 ExceptionState& exceptionState) { |
| 332 DCHECK(isMainThread()); | 333 DCHECK(isMainThread()); |
| 333 | 334 |
| 334 return MediaElementAudioSourceNode::create(*this, *mediaElement, | 335 return MediaElementAudioSourceNode::create(*this, *mediaElement, |
| (...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 546 if (!m_periodicWaveTriangle) | 547 if (!m_periodicWaveTriangle) |
| 547 m_periodicWaveTriangle = PeriodicWave::createTriangle(sampleRate()); | 548 m_periodicWaveTriangle = PeriodicWave::createTriangle(sampleRate()); |
| 548 return m_periodicWaveTriangle; | 549 return m_periodicWaveTriangle; |
| 549 default: | 550 default: |
| 550 NOTREACHED(); | 551 NOTREACHED(); |
| 551 return nullptr; | 552 return nullptr; |
| 552 } | 553 } |
| 553 } | 554 } |
| 554 | 555 |
| 555 String BaseAudioContext::state() const { | 556 String BaseAudioContext::state() const { |
| 556 // These strings had better match the strings for AudioContextState in AudioCo
ntext.idl. | 557 // These strings had better match the strings for AudioContextState in |
| 558 // AudioContext.idl. |
| 557 switch (m_contextState) { | 559 switch (m_contextState) { |
| 558 case Suspended: | 560 case Suspended: |
| 559 return "suspended"; | 561 return "suspended"; |
| 560 case Running: | 562 case Running: |
| 561 return "running"; | 563 return "running"; |
| 562 case Closed: | 564 case Closed: |
| 563 return "closed"; | 565 return "closed"; |
| 564 } | 566 } |
| 565 NOTREACHED(); | 567 NOTREACHED(); |
| 566 return ""; | 568 return ""; |
| 567 } | 569 } |
| 568 | 570 |
| 569 void BaseAudioContext::setContextState(AudioContextState newState) { | 571 void BaseAudioContext::setContextState(AudioContextState newState) { |
| 570 DCHECK(isMainThread()); | 572 DCHECK(isMainThread()); |
| 571 | 573 |
| 572 // Validate the transitions. The valid transitions are Suspended->Running, Ru
nning->Suspended, | 574 // Validate the transitions. The valid transitions are Suspended->Running, |
| 573 // and anything->Closed. | 575 // Running->Suspended, and anything->Closed. |
| 574 switch (newState) { | 576 switch (newState) { |
| 575 case Suspended: | 577 case Suspended: |
| 576 DCHECK_EQ(m_contextState, Running); | 578 DCHECK_EQ(m_contextState, Running); |
| 577 break; | 579 break; |
| 578 case Running: | 580 case Running: |
| 579 DCHECK_EQ(m_contextState, Suspended); | 581 DCHECK_EQ(m_contextState, Suspended); |
| 580 break; | 582 break; |
| 581 case Closed: | 583 case Closed: |
| 582 DCHECK_NE(m_contextState, Closed); | 584 DCHECK_NE(m_contextState, Closed); |
| 583 break; | 585 break; |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 678 AudioBufferSourceNode* sourceNode = | 680 AudioBufferSourceNode* sourceNode = |
| 679 static_cast<AudioBufferSourceNode*>(node); | 681 static_cast<AudioBufferSourceNode*>(node); |
| 680 sourceNode->audioBufferSourceHandler().handleStoppableSourceNode(); | 682 sourceNode->audioBufferSourceHandler().handleStoppableSourceNode(); |
| 681 } | 683 } |
| 682 } | 684 } |
| 683 } | 685 } |
| 684 | 686 |
| 685 void BaseAudioContext::handlePreRenderTasks() { | 687 void BaseAudioContext::handlePreRenderTasks() { |
| 686 DCHECK(isAudioThread()); | 688 DCHECK(isAudioThread()); |
| 687 | 689 |
| 688 // At the beginning of every render quantum, try to update the internal render
ing graph state (from main thread changes). | 690 // At the beginning of every render quantum, try to update the internal |
| 689 // It's OK if the tryLock() fails, we'll just take slightly longer to pick up
the changes. | 691 // rendering graph state (from main thread changes). It's OK if the tryLock() |
| 692 // fails, we'll just take slightly longer to pick up the changes. |
| 690 if (tryLock()) { | 693 if (tryLock()) { |
| 691 deferredTaskHandler().handleDeferredTasks(); | 694 deferredTaskHandler().handleDeferredTasks(); |
| 692 | 695 |
| 693 resolvePromisesForResume(); | 696 resolvePromisesForResume(); |
| 694 | 697 |
| 695 // Check to see if source nodes can be stopped because the end time has pass
ed. | 698 // Check to see if source nodes can be stopped because the end time has |
| 699 // passed. |
| 696 handleStoppableSourceNodes(); | 700 handleStoppableSourceNodes(); |
| 697 | 701 |
| 698 // Update the dirty state of the listener. | 702 // Update the dirty state of the listener. |
| 699 listener()->updateState(); | 703 listener()->updateState(); |
| 700 | 704 |
| 701 unlock(); | 705 unlock(); |
| 702 } | 706 } |
| 703 } | 707 } |
| 704 | 708 |
| 705 void BaseAudioContext::handlePostRenderTasks() { | 709 void BaseAudioContext::handlePostRenderTasks() { |
| 706 DCHECK(isAudioThread()); | 710 DCHECK(isAudioThread()); |
| 707 | 711 |
| 708 // Must use a tryLock() here too. Don't worry, the lock will very rarely be c
ontended and this method is called frequently. | 712 // Must use a tryLock() here too. Don't worry, the lock will very rarely be |
| 709 // The worst that can happen is that there will be some nodes which will take
slightly longer than usual to be deleted or removed | 713 // contended and this method is called frequently. The worst that can happen |
| 710 // from the render graph (in which case they'll render silence). | 714 // is that there will be some nodes which will take slightly longer than usual |
| 715 // to be deleted or removed from the render graph (in which case they'll |
| 716 // render silence). |
| 711 if (tryLock()) { | 717 if (tryLock()) { |
| 712 // Take care of AudioNode tasks where the tryLock() failed previously. | 718 // Take care of AudioNode tasks where the tryLock() failed previously. |
| 713 deferredTaskHandler().breakConnections(); | 719 deferredTaskHandler().breakConnections(); |
| 714 | 720 |
| 715 // Dynamically clean up nodes which are no longer needed. | 721 // Dynamically clean up nodes which are no longer needed. |
| 716 releaseFinishedSourceNodes(); | 722 releaseFinishedSourceNodes(); |
| 717 | 723 |
| 718 deferredTaskHandler().handleDeferredTasks(); | 724 deferredTaskHandler().handleDeferredTasks(); |
| 719 deferredTaskHandler().requestToDeleteHandlersOnMainThread(); | 725 deferredTaskHandler().requestToDeleteHandlersOnMainThread(); |
| 720 | 726 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 733 } else { | 739 } else { |
| 734 resolver->resolve(); | 740 resolver->resolve(); |
| 735 } | 741 } |
| 736 } | 742 } |
| 737 | 743 |
| 738 m_resumeResolvers.clear(); | 744 m_resumeResolvers.clear(); |
| 739 m_isResolvingResumePromises = false; | 745 m_isResolvingResumePromises = false; |
| 740 } | 746 } |
| 741 | 747 |
| 742 void BaseAudioContext::resolvePromisesForResume() { | 748 void BaseAudioContext::resolvePromisesForResume() { |
| 743 // This runs inside the BaseAudioContext's lock when handling pre-render tasks
. | 749 // This runs inside the BaseAudioContext's lock when handling pre-render |
| 750 // tasks. |
| 744 DCHECK(isAudioThread()); | 751 DCHECK(isAudioThread()); |
| 745 ASSERT(isGraphOwner()); | 752 ASSERT(isGraphOwner()); |
| 746 | 753 |
| 747 // Resolve any pending promises created by resume(). Only do this if we haven'
t already started | 754 // Resolve any pending promises created by resume(). Only do this if we |
| 748 // resolving these promises. This gets called very often and it takes some tim
e to resolve the | 755 // haven't already started resolving these promises. This gets called very |
| 749 // promises in the main thread. | 756 // often and it takes some time to resolve the promises in the main thread. |
| 750 if (!m_isResolvingResumePromises && m_resumeResolvers.size() > 0) { | 757 if (!m_isResolvingResumePromises && m_resumeResolvers.size() > 0) { |
| 751 m_isResolvingResumePromises = true; | 758 m_isResolvingResumePromises = true; |
| 752 Platform::current()->mainThread()->getWebTaskRunner()->postTask( | 759 Platform::current()->mainThread()->getWebTaskRunner()->postTask( |
| 753 BLINK_FROM_HERE, | 760 BLINK_FROM_HERE, |
| 754 crossThreadBind(&BaseAudioContext::resolvePromisesForResumeOnMainThread, | 761 crossThreadBind(&BaseAudioContext::resolvePromisesForResumeOnMainThread, |
| 755 wrapCrossThreadPersistent(this))); | 762 wrapCrossThreadPersistent(this))); |
| 756 } | 763 } |
| 757 } | 764 } |
| 758 | 765 |
| 759 void BaseAudioContext::rejectPendingDecodeAudioDataResolvers() { | 766 void BaseAudioContext::rejectPendingDecodeAudioDataResolvers() { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 780 ->addConsoleMessage(ConsoleMessage::create( | 787 ->addConsoleMessage(ConsoleMessage::create( |
| 781 JSMessageSource, WarningMessageLevel, | 788 JSMessageSource, WarningMessageLevel, |
| 782 "An AudioContext in a cross origin iframe must be created or resumed " | 789 "An AudioContext in a cross origin iframe must be created or resumed " |
| 783 "from a user gesture to enable audio output.")); | 790 "from a user gesture to enable audio output.")); |
| 784 return false; | 791 return false; |
| 785 } | 792 } |
| 786 | 793 |
| 787 void BaseAudioContext::rejectPendingResolvers() { | 794 void BaseAudioContext::rejectPendingResolvers() { |
| 788 DCHECK(isMainThread()); | 795 DCHECK(isMainThread()); |
| 789 | 796 |
| 790 // Audio context is closing down so reject any resume promises that are still
pending. | 797 // Audio context is closing down so reject any resume promises that are still |
| 798 // pending. |
| 791 | 799 |
| 792 for (auto& resolver : m_resumeResolvers) { | 800 for (auto& resolver : m_resumeResolvers) { |
| 793 resolver->reject( | 801 resolver->reject( |
| 794 DOMException::create(InvalidStateError, "Audio context is going away")); | 802 DOMException::create(InvalidStateError, "Audio context is going away")); |
| 795 } | 803 } |
| 796 m_resumeResolvers.clear(); | 804 m_resumeResolvers.clear(); |
| 797 m_isResolvingResumePromises = false; | 805 m_isResolvingResumePromises = false; |
| 798 | 806 |
| 799 rejectPendingDecodeAudioDataResolvers(); | 807 rejectPendingDecodeAudioDataResolvers(); |
| 800 } | 808 } |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 835 } | 843 } |
| 836 | 844 |
| 837 SecurityOrigin* BaseAudioContext::getSecurityOrigin() const { | 845 SecurityOrigin* BaseAudioContext::getSecurityOrigin() const { |
| 838 if (getExecutionContext()) | 846 if (getExecutionContext()) |
| 839 return getExecutionContext()->getSecurityOrigin(); | 847 return getExecutionContext()->getSecurityOrigin(); |
| 840 | 848 |
| 841 return nullptr; | 849 return nullptr; |
| 842 } | 850 } |
| 843 | 851 |
| 844 } // namespace blink | 852 } // namespace blink |
| OLD | NEW |