| Index: Source/modules/webaudio/AudioContext.cpp
|
| diff --git a/Source/modules/webaudio/AudioContext.cpp b/Source/modules/webaudio/AudioContext.cpp
|
| index 306485e48560889c5626c5c53bb6a3836b9f377e..e4fd7b7dee28250b02e6a236100dc514e36243b3 100644
|
| --- a/Source/modules/webaudio/AudioContext.cpp
|
| +++ b/Source/modules/webaudio/AudioContext.cpp
|
| @@ -115,6 +115,7 @@ AudioContext::AudioContext(Document* document)
|
| , m_isOfflineContext(false)
|
| , m_contextState(Suspended)
|
| , m_cachedSampleFrame(0)
|
| + , m_closedContextSampleRate(-1)
|
| {
|
| m_didInitializeContextGraphMutex = true;
|
| m_destinationNode = DefaultAudioDestinationNode::create(this);
|
| @@ -136,6 +137,7 @@ AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t
|
| , m_isOfflineContext(true)
|
| , m_contextState(Suspended)
|
| , m_cachedSampleFrame(0)
|
| + , m_closedContextSampleRate(-1)
|
| {
|
| m_didInitializeContextGraphMutex = true;
|
| // Create a new destination for offline rendering.
|
| @@ -152,12 +154,14 @@ AudioContext::~AudioContext()
|
| fprintf(stderr, "%p: AudioContext::~AudioContext(): %u\n", this, m_contextId);
|
| #endif
|
| // AudioNodes keep a reference to their context, so there should be no way to be in the destructor if there are still AudioNodes around.
|
| +
|
| ASSERT(!m_isInitialized);
|
| ASSERT(!m_referencedNodes.size());
|
| ASSERT(!m_finishedNodes.size());
|
| ASSERT(!m_suspendResolvers.size());
|
| ASSERT(!m_isResolvingResumePromises);
|
| ASSERT(!m_resumeResolvers.size());
|
| + ASSERT(!m_audioDecoderResolvers.size());
|
| }
|
|
|
| void AudioContext::initialize()
|
| @@ -233,6 +237,14 @@ void AudioContext::uninitialize()
|
| ASSERT(m_listener);
|
| m_listener->waitForHRTFDatabaseLoaderThreadCompletion();
|
|
|
| + // Reject any decodeAudioData promises that haven't been fulfilled yet.
|
| + for (auto& resolver : m_audioDecoderResolvers) {
|
| + resolver->reject(DOMException::create(InvalidStateError, "Audio context is going away"));
|
| + }
|
| + m_audioDecoderResolvers.clear();
|
| +
|
| + // Uninitialization done, so clear flags to indicate that the AudioContext has no pending
|
| + // activity anymore.
|
| clear();
|
| }
|
|
|
| @@ -269,20 +281,30 @@ AudioBuffer* AudioContext::createBuffer(unsigned numberOfChannels, size_t number
|
| return AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate, exceptionState);
|
| }
|
|
|
| -void AudioContext::decodeAudioData(DOMArrayBuffer* audioData, AudioBufferCallback* successCallback, AudioBufferCallback* errorCallback, ExceptionState& exceptionState)
|
| +ScriptPromise AudioContext::decodeAudioData(ScriptState* scriptState, DOMArrayBuffer* audioData, AudioBufferCallback* successCallback, AudioBufferCallback* errorCallback, ExceptionState& exceptionState)
|
| {
|
| - if (isContextClosed()) {
|
| - throwExceptionForClosedState(exceptionState);
|
| - return;
|
| - }
|
| -
|
| if (!audioData) {
|
| - exceptionState.throwDOMException(
|
| - SyntaxError,
|
| + RefPtrWillBeRawPtr<DOMException> error = DOMException::create(
|
| + NotSupportedError,
|
| "invalid ArrayBuffer for audioData.");
|
| - return;
|
| + if (errorCallback) {
|
| + errorCallback->handleEvent(error.get());
|
| + }
|
| + return ScriptPromise::rejectWithDOMException(scriptState, error);
|
| }
|
| - m_audioDecoder.decodeAsync(audioData, sampleRate(), successCallback, errorCallback);
|
| +
|
| + RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
|
| + ScriptPromise promise = resolver->promise();
|
| +
|
| + m_audioDecoderResolvers.append(resolver);
|
| +
|
| + float rate = isContextClosed() ? m_closedContextSampleRate : sampleRate();
|
| +
|
| + ASSERT(rate > 0);
|
| +
|
| + m_audioDecoder.decodeAsync(audioData, rate, successCallback, errorCallback, resolver.get(), this);
|
| +
|
| + return promise;
|
| }
|
|
|
| AudioBufferSourceNode* AudioContext::createBufferSource(ExceptionState& exceptionState)
|
| @@ -912,6 +934,19 @@ void AudioContext::handleStoppableSourceNodes()
|
| }
|
| }
|
| }
|
| +
|
| +void AudioContext::removeAudioDecoderResolver(ScriptPromiseResolver* resolver)
|
| +{
|
| + ASSERT(isMainThread());
|
| +
|
| + for (size_t k = 0; k < m_audioDecoderResolvers.size(); ++k) {
|
| + if (resolver == m_audioDecoderResolvers.at(k)) {
|
| + m_audioDecoderResolvers.remove(k);
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| void AudioContext::handlePreRenderTasks()
|
| {
|
| ASSERT(isAudioThread());
|
| @@ -1129,8 +1164,7 @@ void AudioContext::rejectPendingResolvers()
|
| {
|
| ASSERT(isMainThread());
|
|
|
| - // Audio context is closing down so reject any suspend or resume promises that are still
|
| - // pending.
|
| + // Audio context is closing down so reject any promises that are still pending.
|
|
|
| for (auto& resolver : m_suspendResolvers) {
|
| resolver->reject(DOMException::create(InvalidStateError, "Audio context is going away"));
|
| @@ -1204,6 +1238,7 @@ void AudioContext::fireCompletionEvent()
|
|
|
| DEFINE_TRACE(AudioContext)
|
| {
|
| + visitor->trace(m_audioDecoderResolvers);
|
| visitor->trace(m_closeResolver);
|
| visitor->trace(m_offlineResolver);
|
| visitor->trace(m_renderTarget);
|
| @@ -1271,6 +1306,9 @@ ScriptPromise AudioContext::closeContext(ScriptState* scriptState)
|
| "Cannot close a context that is being closed or has already been closed."));
|
| }
|
|
|
| + // Save the current sample rate for any subsequent decodeAudioData calls.
|
| + m_closedContextSampleRate = sampleRate();
|
| +
|
| m_closeResolver = ScriptPromiseResolver::create(scriptState);
|
| ScriptPromise promise = m_closeResolver->promise();
|
|
|
|
|