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 12 matching lines...) Expand all Loading... | |
23 */ | 23 */ |
24 | 24 |
25 #include "config.h" | 25 #include "config.h" |
26 | 26 |
27 #if ENABLE(WEB_AUDIO) | 27 #if ENABLE(WEB_AUDIO) |
28 | 28 |
29 #include "modules/webaudio/AudioContext.h" | 29 #include "modules/webaudio/AudioContext.h" |
30 | 30 |
31 #include "bindings/core/v8/ExceptionMessages.h" | 31 #include "bindings/core/v8/ExceptionMessages.h" |
32 #include "bindings/core/v8/ExceptionState.h" | 32 #include "bindings/core/v8/ExceptionState.h" |
33 #include "bindings/core/v8/ScriptState.h" | |
33 #include "core/dom/Document.h" | 34 #include "core/dom/Document.h" |
34 #include "core/dom/ExceptionCode.h" | 35 #include "core/dom/ExceptionCode.h" |
35 #include "core/html/HTMLMediaElement.h" | 36 #include "core/html/HTMLMediaElement.h" |
36 #include "core/inspector/ScriptCallStack.h" | 37 #include "core/inspector/ScriptCallStack.h" |
37 #include "platform/audio/FFTFrame.h" | 38 #include "platform/audio/FFTFrame.h" |
38 #include "platform/audio/HRTFPanner.h" | 39 #include "platform/audio/HRTFPanner.h" |
39 #include "modules/mediastream/MediaStream.h" | 40 #include "modules/mediastream/MediaStream.h" |
40 #include "modules/webaudio/AnalyserNode.h" | 41 #include "modules/webaudio/AnalyserNode.h" |
41 #include "modules/webaudio/AudioBuffer.h" | 42 #include "modules/webaudio/AudioBuffer.h" |
42 #include "modules/webaudio/AudioBufferCallback.h" | 43 #include "modules/webaudio/AudioBufferCallback.h" |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
94 return audioContext; | 95 return audioContext; |
95 } | 96 } |
96 | 97 |
97 // Constructor for rendering to the audio hardware. | 98 // Constructor for rendering to the audio hardware. |
98 AudioContext::AudioContext(Document* document) | 99 AudioContext::AudioContext(Document* document) |
99 : ActiveDOMObject(document) | 100 : ActiveDOMObject(document) |
100 , m_isStopScheduled(false) | 101 , m_isStopScheduled(false) |
101 , m_isCleared(false) | 102 , m_isCleared(false) |
102 , m_isInitialized(false) | 103 , m_isInitialized(false) |
103 , m_destinationNode(nullptr) | 104 , m_destinationNode(nullptr) |
105 , m_isResolvingResumePromises(false) | |
104 , m_automaticPullNodesNeedUpdating(false) | 106 , m_automaticPullNodesNeedUpdating(false) |
105 , m_connectionCount(0) | 107 , m_connectionCount(0) |
106 , m_audioThread(0) | 108 , m_audioThread(0) |
107 , m_isOfflineContext(false) | 109 , m_isOfflineContext(false) |
110 , m_state(Paused) | |
108 { | 111 { |
109 m_destinationNode = DefaultAudioDestinationNode::create(this); | 112 m_destinationNode = DefaultAudioDestinationNode::create(this); |
110 | 113 |
111 initialize(); | 114 initialize(); |
112 #if DEBUG_AUDIONODE_REFERENCES | 115 #if DEBUG_AUDIONODE_REFERENCES |
113 fprintf(stderr, "%p: AudioContext::AudioContext() #%u\n", this, AudioContext ::s_hardwareContextCount); | 116 fprintf(stderr, "%p: AudioContext::AudioContext() #%u\n", this, AudioContext ::s_hardwareContextCount); |
114 #endif | 117 #endif |
115 } | 118 } |
116 | 119 |
117 // Constructor for offline (non-realtime) rendering. | 120 // Constructor for offline (non-realtime) rendering. |
118 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate) | 121 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate) |
119 : ActiveDOMObject(document) | 122 : ActiveDOMObject(document) |
120 , m_isStopScheduled(false) | 123 , m_isStopScheduled(false) |
121 , m_isCleared(false) | 124 , m_isCleared(false) |
122 , m_isInitialized(false) | 125 , m_isInitialized(false) |
123 , m_destinationNode(nullptr) | 126 , m_destinationNode(nullptr) |
127 , m_isResolvingResumePromises(false) | |
124 , m_automaticPullNodesNeedUpdating(false) | 128 , m_automaticPullNodesNeedUpdating(false) |
125 , m_connectionCount(0) | 129 , m_connectionCount(0) |
126 , m_audioThread(0) | 130 , m_audioThread(0) |
127 , m_isOfflineContext(true) | 131 , m_isOfflineContext(true) |
132 , m_state(Paused) | |
128 { | 133 { |
129 // Create a new destination for offline rendering. | 134 // Create a new destination for offline rendering. |
130 m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampl eRate); | 135 m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampl eRate); |
131 if (m_renderTarget.get()) | 136 if (m_renderTarget.get()) |
132 m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTa rget.get()); | 137 m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTa rget.get()); |
133 | 138 |
134 initialize(); | 139 initialize(); |
135 } | 140 } |
136 | 141 |
137 AudioContext::~AudioContext() | 142 AudioContext::~AudioContext() |
(...skipping 20 matching lines...) Expand all Loading... | |
158 m_listener = AudioListener::create(); | 163 m_listener = AudioListener::create(); |
159 | 164 |
160 if (m_destinationNode.get()) { | 165 if (m_destinationNode.get()) { |
161 m_destinationNode->initialize(); | 166 m_destinationNode->initialize(); |
162 | 167 |
163 if (!isOfflineContext()) { | 168 if (!isOfflineContext()) { |
164 // This starts the audio thread. The destination node's provideInput () method will now be called repeatedly to render audio. | 169 // This starts the audio thread. The destination node's provideInput () method will now be called repeatedly to render audio. |
165 // Each time provideInput() is called, a portion of the audio stream is rendered. Let's call this time period a "render quantum". | 170 // Each time provideInput() is called, a portion of the audio stream is rendered. Let's call this time period a "render quantum". |
166 // NOTE: for now default AudioContext does not need an explicit star tRendering() call from JavaScript. | 171 // NOTE: for now default AudioContext does not need an explicit star tRendering() call from JavaScript. |
167 // We may want to consider requiring it for symmetry with OfflineAud ioContext. | 172 // We may want to consider requiring it for symmetry with OfflineAud ioContext. |
168 m_destinationNode->startRendering(); | 173 m_destinationNode->startRendering(); |
haraken
2014/10/15 15:36:26
You can call startRendering() instead of m_destina
Raymond Toy
2014/10/15 19:50:50
Done.
| |
174 setState(Running); | |
haraken
2014/10/15 15:36:26
Then you can remove this. It's important to reduce
Raymond Toy
2014/10/15 19:50:49
Done.
| |
169 ++s_hardwareContextCount; | 175 ++s_hardwareContextCount; |
170 } | 176 } |
171 | 177 |
172 m_isInitialized = true; | 178 m_isInitialized = true; |
173 } | 179 } |
174 } | 180 } |
175 | 181 |
176 void AudioContext::clear() | 182 void AudioContext::clear() |
177 { | 183 { |
178 // We need to run disposers before destructing m_contextGraphMutex. | 184 // We need to run disposers before destructing m_contextGraphMutex. |
(...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
524 exceptionState.throwDOMException( | 530 exceptionState.throwDOMException( |
525 IndexSizeError, | 531 IndexSizeError, |
526 "length of imaginary array (" + String::number(imag->length()) | 532 "length of imaginary array (" + String::number(imag->length()) |
527 + ") exceeds allowed maximum of 4096"); | 533 + ") exceeds allowed maximum of 4096"); |
528 return 0; | 534 return 0; |
529 } | 535 } |
530 | 536 |
531 return PeriodicWave::create(sampleRate(), real, imag); | 537 return PeriodicWave::create(sampleRate(), real, imag); |
532 } | 538 } |
533 | 539 |
540 String AudioContext::state() const | |
541 { | |
542 switch (m_state) { | |
543 case Paused: | |
544 return "paused"; | |
545 case Running: | |
546 return "running"; | |
547 case Released: | |
548 return "released"; | |
549 } | |
550 ASSERT_NOT_REACHED(); | |
551 return "running"; | |
haraken
2014/10/15 15:36:26
Nit: I'd return "".
Raymond Toy
2014/10/15 19:50:50
Done.
| |
552 } | |
553 | |
554 void AudioContext::setState(AudioContextState newState) | |
555 { | |
556 // Validate the transitions | |
557 switch (newState) { | |
558 case Paused: | |
559 ASSERT(m_state == Running); | |
560 break; | |
561 case Running: | |
562 ASSERT(m_state == Paused); | |
563 break; | |
564 case Released: | |
565 break; | |
haraken
2014/10/15 15:36:26
Shall we add ASSERT(m_state == Released) ?
Raymond Toy
2014/10/15 16:31:56
When the close() method is implemented, it will be
| |
566 } | |
567 | |
568 m_state = newState; | |
569 } | |
570 | |
571 void AudioContext::suspendContext(ExceptionState& exceptionState) | |
572 { | |
573 ASSERT(isMainThread()); | |
574 AutoLocker locker(this); | |
575 | |
576 if (m_state == Released) { | |
577 exceptionState.throwDOMException( | |
578 InvalidStateError, | |
579 "cannot suspend an AudioContext that has been released"); | |
haraken
2014/10/15 15:36:26
Nit: You can immediately return here. Then you don
Raymond Toy
2014/10/15 19:50:50
Done.
| |
580 } else { | |
581 if (m_destinationNode && !isOfflineContext()) { | |
582 m_destinationNode->stop(); | |
583 setState(Paused); | |
haraken
2014/10/15 15:36:26
Probably you want to implement a helper method for
Raymond Toy
2014/10/15 19:50:50
Done.
| |
584 } | |
585 } | |
586 } | |
587 | |
588 ScriptPromise AudioContext::resumeContext(ScriptState* scriptState) | |
589 { | |
590 ASSERT(isMainThread()); | |
591 AutoLocker locker(this); | |
592 | |
593 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scrip tState); | |
594 | |
595 ScriptPromise promise = resolver->promise(); | |
596 | |
597 if (isOfflineContext()) { | |
598 // For offline context, resolve now, but reject if the context has been released. | |
599 if (m_state == Released) | |
600 resolver->reject(); | |
601 else | |
602 resolver->resolve(); | |
603 } else { | |
604 // Restart the destination node to pull on the audio graph. | |
605 if (m_destinationNode) { | |
606 m_destinationNode->startRendering(); | |
haraken
2014/10/15 15:36:26
You can call startRendering() instead of m_destina
Raymond Toy
2014/10/15 19:50:50
Done.
| |
607 setState(Running); | |
haraken
2014/10/15 15:36:26
Then you can remove this. We want to reduce the ca
Raymond Toy
2014/10/15 19:50:50
Done.
| |
608 } | |
609 | |
610 // Save the promise which will get resolved when the destination node st arts pulling on the | |
611 // graph again. | |
612 m_resumePromises.append(resolver); | |
613 } | |
614 | |
615 return promise; | |
616 } | |
617 | |
534 void AudioContext::notifyNodeFinishedProcessing(AudioNode* node) | 618 void AudioContext::notifyNodeFinishedProcessing(AudioNode* node) |
535 { | 619 { |
536 ASSERT(isAudioThread()); | 620 ASSERT(isAudioThread()); |
537 m_finishedNodes.append(node); | 621 m_finishedNodes.append(node); |
538 } | 622 } |
539 | 623 |
540 void AudioContext::derefFinishedSourceNodes() | 624 void AudioContext::derefFinishedSourceNodes() |
541 { | 625 { |
542 ASSERT(isGraphOwner()); | 626 ASSERT(isGraphOwner()); |
543 ASSERT(isAudioThread()); | 627 ASSERT(isAudioThread()); |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
630 // It's OK if the tryLock() fails, we'll just take slightly longer to pick u p the changes. | 714 // It's OK if the tryLock() fails, we'll just take slightly longer to pick u p the changes. |
631 if (tryLock()) { | 715 if (tryLock()) { |
632 // Update the channel count mode. | 716 // Update the channel count mode. |
633 updateChangedChannelCountMode(); | 717 updateChangedChannelCountMode(); |
634 | 718 |
635 // Fixup the state of any dirty AudioSummingJunctions and AudioNodeOutpu ts. | 719 // Fixup the state of any dirty AudioSummingJunctions and AudioNodeOutpu ts. |
636 handleDirtyAudioSummingJunctions(); | 720 handleDirtyAudioSummingJunctions(); |
637 handleDirtyAudioNodeOutputs(); | 721 handleDirtyAudioNodeOutputs(); |
638 | 722 |
639 updateAutomaticPullNodes(); | 723 updateAutomaticPullNodes(); |
724 resolvePromisesForResume(); | |
725 | |
640 unlock(); | 726 unlock(); |
641 } | 727 } |
642 } | 728 } |
643 | 729 |
644 void AudioContext::handlePostRenderTasks() | 730 void AudioContext::handlePostRenderTasks() |
645 { | 731 { |
646 ASSERT(isAudioThread()); | 732 ASSERT(isAudioThread()); |
647 | 733 |
648 // Must use a tryLock() here too. Don't worry, the lock will very rarely be contended and this method is called frequently. | 734 // Must use a tryLock() here too. Don't worry, the lock will very rarely be contended and this method is called frequently. |
649 // The worst that can happen is that there will be some nodes which will tak e slightly longer than usual to be deleted or removed | 735 // The worst that can happen is that there will be some nodes which will tak e slightly longer than usual to be deleted or removed |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
795 } | 881 } |
796 | 882 |
797 void AudioContext::processAutomaticPullNodes(size_t framesToProcess) | 883 void AudioContext::processAutomaticPullNodes(size_t framesToProcess) |
798 { | 884 { |
799 ASSERT(isAudioThread()); | 885 ASSERT(isAudioThread()); |
800 | 886 |
801 for (unsigned i = 0; i < m_renderingAutomaticPullNodes.size(); ++i) | 887 for (unsigned i = 0; i < m_renderingAutomaticPullNodes.size(); ++i) |
802 m_renderingAutomaticPullNodes[i]->processIfNecessary(framesToProcess); | 888 m_renderingAutomaticPullNodes[i]->processIfNecessary(framesToProcess); |
803 } | 889 } |
804 | 890 |
891 void AudioContext::resolvePromisesForResumeOnMainThread() | |
892 { | |
893 ASSERT(isMainThread()); | |
894 AutoLocker locker(this); | |
895 | |
896 for (unsigned k = 0; k < m_resumePromises.size(); ++k) { | |
897 if (m_state == Released) | |
898 m_resumePromises[k]->reject(); | |
yhirano
2014/10/15 09:02:27
[opt] In general, it would be better to reject a p
Raymond Toy
2014/10/15 19:50:49
Good idea. I've added an Exception. The spec isn'
| |
899 else | |
900 m_resumePromises[k]->resolve(); | |
901 } | |
902 | |
903 m_resumePromises.clear(); | |
904 m_isResolvingResumePromises = false; | |
905 } | |
906 | |
907 void AudioContext::resolvePromisesForResume() | |
908 { | |
909 // This is run inside the AudioContext's lock when handling pre-render tasks . | |
haraken
2014/10/15 15:36:26
Nit: is run => runs
Raymond Toy
2014/10/15 19:50:50
Done.
| |
910 ASSERT(isAudioThread()); | |
haraken
2014/10/15 15:36:26
Shall we add ASSERT(isGraphOwner()) ?
Raymond Toy
2014/10/15 19:50:50
Done.
| |
911 | |
912 // Resolve any pending promises created by resume(). Only do this we if have n't already started | |
913 // resolving these promises. This gets called very often and it takes some t ime to resolve the | |
914 // promises in the main thread. | |
915 if (!m_isResolvingResumePromises && m_resumePromises.size() > 0) { | |
916 m_isResolvingResumePromises = true; | |
917 callOnMainThread(bind(&AudioContext::resolvePromisesForResumeOnMainThrea d, this)); | |
918 } | |
919 } | |
920 | |
805 const AtomicString& AudioContext::interfaceName() const | 921 const AtomicString& AudioContext::interfaceName() const |
806 { | 922 { |
807 return EventTargetNames::AudioContext; | 923 return EventTargetNames::AudioContext; |
808 } | 924 } |
809 | 925 |
810 ExecutionContext* AudioContext::executionContext() const | 926 ExecutionContext* AudioContext::executionContext() const |
811 { | 927 { |
812 return m_isStopScheduled ? 0 : ActiveDOMObject::executionContext(); | 928 return m_isStopScheduled ? 0 : ActiveDOMObject::executionContext(); |
813 } | 929 } |
814 | 930 |
815 void AudioContext::startRendering() | 931 void AudioContext::startRendering() |
816 { | 932 { |
933 ASSERT(isMainThread()); | |
817 destination()->startRendering(); | 934 destination()->startRendering(); |
935 setState(Running); | |
818 } | 936 } |
819 | 937 |
820 void AudioContext::fireCompletionEvent() | 938 void AudioContext::fireCompletionEvent() |
821 { | 939 { |
822 ASSERT(isMainThread()); | 940 ASSERT(isMainThread()); |
823 if (!isMainThread()) | 941 if (!isMainThread()) |
824 return; | 942 return; |
825 | 943 |
826 AudioBuffer* renderedBuffer = m_renderTarget.get(); | 944 AudioBuffer* renderedBuffer = m_renderTarget.get(); |
827 | 945 |
946 setState(Released); | |
947 | |
828 ASSERT(renderedBuffer); | 948 ASSERT(renderedBuffer); |
829 if (!renderedBuffer) | 949 if (!renderedBuffer) |
830 return; | 950 return; |
831 | 951 |
832 // Avoid firing the event if the document has already gone away. | 952 // Avoid firing the event if the document has already gone away. |
833 if (executionContext()) { | 953 if (executionContext()) { |
834 // Call the offline rendering completion event listener. | 954 // Call the offline rendering completion event listener. |
835 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer)); | 955 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer)); |
836 } | 956 } |
837 } | 957 } |
(...skipping 29 matching lines...) Expand all Loading... | |
867 | 987 |
868 for (HashSet<AudioNode*>::iterator k = m_deferredCountModeChange.begin(); k != m_deferredCountModeChange.end(); ++k) | 988 for (HashSet<AudioNode*>::iterator k = m_deferredCountModeChange.begin(); k != m_deferredCountModeChange.end(); ++k) |
869 (*k)->updateChannelCountMode(); | 989 (*k)->updateChannelCountMode(); |
870 | 990 |
871 m_deferredCountModeChange.clear(); | 991 m_deferredCountModeChange.clear(); |
872 } | 992 } |
873 | 993 |
874 } // namespace blink | 994 } // namespace blink |
875 | 995 |
876 #endif // ENABLE(WEB_AUDIO) | 996 #endif // ENABLE(WEB_AUDIO) |
OLD | NEW |