5ef86afcd81de4fa8185b43200f11440f0ccd34f
[WebKit-https.git] / Source / WebCore / Modules / webaudio / OfflineAudioDestinationNode.cpp
1 /*
2  * Copyright (C) 2011, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "config.h"
26
27 #if ENABLE(WEB_AUDIO)
28
29 #include "OfflineAudioDestinationNode.h"
30
31 #include "AudioBus.h"
32 #include "AudioContext.h"
33 #include "AudioUtilities.h"
34 #include "AudioWorklet.h"
35 #include "AudioWorkletMessagingProxy.h"
36 #include "HRTFDatabaseLoader.h"
37 #include "OfflineAudioContext.h"
38 #include "WorkerRunLoop.h"
39 #include <algorithm>
40 #include <wtf/IsoMallocInlines.h>
41 #include <wtf/MainThread.h>
42 #include <wtf/threads/BinarySemaphore.h>
43  
44 namespace WebCore {
45
46 WTF_MAKE_ISO_ALLOCATED_IMPL(OfflineAudioDestinationNode);
47
48 OfflineAudioDestinationNode::OfflineAudioDestinationNode(OfflineAudioContext& context, unsigned numberOfChannels, float sampleRate, RefPtr<AudioBuffer>&& renderTarget)
49     : AudioDestinationNode(context, sampleRate)
50     , m_numberOfChannels(numberOfChannels)
51     , m_renderTarget(WTFMove(renderTarget))
52     , m_framesToProcess(m_renderTarget ? m_renderTarget->length() : 0)
53 {
54     m_renderBus = AudioBus::create(numberOfChannels, AudioUtilities::renderQuantumSize);
55     initializeDefaultNodeOptions(numberOfChannels, ChannelCountMode::Explicit, ChannelInterpretation::Speakers);
56 }
57
58 OfflineAudioDestinationNode::~OfflineAudioDestinationNode()
59 {
60     uninitialize();
61 }
62
63 OfflineAudioContext& OfflineAudioDestinationNode::context()
64 {
65     return downcast<OfflineAudioContext>(AudioDestinationNode::context());
66 }
67
68 const OfflineAudioContext& OfflineAudioDestinationNode::context() const
69 {
70     return downcast<OfflineAudioContext>(AudioDestinationNode::context());
71 }
72
73 unsigned OfflineAudioDestinationNode::maxChannelCount() const
74 {
75     return m_numberOfChannels;
76 }
77
78 void OfflineAudioDestinationNode::initialize()
79 {
80     if (isInitialized())
81         return;
82
83     AudioNode::initialize();
84 }
85
86 void OfflineAudioDestinationNode::uninitialize()
87 {
88     if (!isInitialized())
89         return;
90
91     if (m_startedRendering) {
92         if (m_renderThread) {
93             m_renderThread->waitForCompletion();
94             m_renderThread = nullptr;
95         }
96         if (auto* workletProxy = context().audioWorklet().proxy()) {
97             BinarySemaphore semaphore;
98             workletProxy->postTaskForModeToWorkletGlobalScope([&semaphore](ScriptExecutionContext&) mutable {
99                 semaphore.signal();
100             }, WorkerRunLoop::defaultMode());
101             semaphore.wait();
102         }
103     }
104
105     AudioNode::uninitialize();
106 }
107
108 void OfflineAudioDestinationNode::startRendering(CompletionHandler<void(Optional<Exception>&&)>&& completionHandler)
109 {
110     ALWAYS_LOG(LOGIDENTIFIER);
111
112     ASSERT(isMainThread());
113     ASSERT(m_renderTarget.get());
114     if (!m_renderTarget.get())
115         return completionHandler(Exception { InvalidStateError, "OfflineAudioContextNode has no rendering buffer"_s });
116     
117     if (m_startedRendering)
118         return completionHandler(Exception { InvalidStateError, "Already started rendering"_s });
119
120     m_startedRendering = true;
121     auto protectedThis = makeRef(*this);
122
123     auto offThreadRendering = [this, protectedThis = WTFMove(protectedThis)]() mutable {
124         auto result = offlineRender();
125         callOnMainThread([this, result, currentSampleFrame = m_currentSampleFrame.load(), protectedThis = WTFMove(protectedThis)]() mutable {
126             context().postTask([this, protectedThis = WTFMove(protectedThis), result, currentSampleFrame]() mutable {
127                 m_startedRendering = false;
128                 switch (result) {
129                 case OfflineRenderResult::Failure:
130                     context().finishedRendering(false);
131                     break;
132                 case OfflineRenderResult::Complete:
133                     context().finishedRendering(true);
134                     break;
135                 case OfflineRenderResult::Suspended:
136                     context().didSuspendRendering(currentSampleFrame);
137                     break;
138                 }
139             });
140         });
141     };
142
143     if (auto* workletProxy = context().audioWorklet().proxy()) {
144         workletProxy->postTaskForModeToWorkletGlobalScope([offThreadRendering = WTFMove(offThreadRendering)](ScriptExecutionContext&) mutable {
145             offThreadRendering();
146         }, WorkerRunLoop::defaultMode());
147         return completionHandler(WTF::nullopt);
148     }
149
150     // FIXME: We should probably limit the number of threads we create for offline audio.
151     m_renderThread = Thread::create("offline renderer", WTFMove(offThreadRendering), ThreadType::Audio, Thread::QOS::Default);
152     completionHandler(WTF::nullopt);
153 }
154
155 auto OfflineAudioDestinationNode::offlineRender() -> OfflineRenderResult
156 {
157     ASSERT(!isMainThread());
158     ASSERT(m_renderBus.get());
159
160     if (!m_renderBus.get())
161         return OfflineRenderResult::Failure;
162
163     RELEASE_ASSERT(context().isInitialized());
164
165     bool channelsMatch = m_renderBus->numberOfChannels() == m_renderTarget->numberOfChannels();
166     ASSERT(channelsMatch);
167     if (!channelsMatch)
168         return OfflineRenderResult::Failure;
169
170     bool isRenderBusAllocated = m_renderBus->length() >= AudioUtilities::renderQuantumSize;
171     ASSERT(isRenderBusAllocated);
172     if (!isRenderBusAllocated)
173         return OfflineRenderResult::Failure;
174
175     // Break up the render target into smaller "render quantize" sized pieces.
176     // Render until we're finished.
177     unsigned numberOfChannels = m_renderTarget->numberOfChannels();
178
179     while (m_framesToProcess > 0) {
180         if (context().shouldSuspend())
181             return OfflineRenderResult::Suspended;
182
183         // Render one render quantum.
184         render(0, m_renderBus.get(), AudioUtilities::renderQuantumSize, { });
185         
186         size_t framesAvailableToCopy = std::min(m_framesToProcess, AudioUtilities::renderQuantumSize);
187         
188         for (unsigned channelIndex = 0; channelIndex < numberOfChannels; ++channelIndex) {
189             const float* source = m_renderBus->channel(channelIndex)->data();
190             float* destination = m_renderTarget->channelData(channelIndex)->data();
191             memcpy(destination + m_destinationOffset, source, sizeof(float) * framesAvailableToCopy);
192         }
193         
194         m_destinationOffset += framesAvailableToCopy;
195         m_framesToProcess -= framesAvailableToCopy;
196     }
197
198     return OfflineRenderResult::Complete;
199 }
200
201 } // namespace WebCore
202
203 #endif // ENABLE(WEB_AUDIO)