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