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 "HRTFDatabaseLoader.h" |
34 | #include <algorithm> |
35 | #include <wtf/IsoMallocInlines.h> |
36 | #include <wtf/MainThread.h> |
37 | |
38 | namespace WebCore { |
39 | |
40 | WTF_MAKE_ISO_ALLOCATED_IMPL(OfflineAudioDestinationNode); |
41 | |
42 | const size_t renderQuantumSize = 128; |
43 | |
44 | OfflineAudioDestinationNode::OfflineAudioDestinationNode(AudioContext& context, AudioBuffer* renderTarget) |
45 | : AudioDestinationNode(context, renderTarget->sampleRate()) |
46 | , m_renderTarget(renderTarget) |
47 | , m_startedRendering(false) |
48 | { |
49 | m_renderBus = AudioBus::create(renderTarget->numberOfChannels(), renderQuantumSize); |
50 | } |
51 | |
52 | OfflineAudioDestinationNode::~OfflineAudioDestinationNode() |
53 | { |
54 | uninitialize(); |
55 | } |
56 | |
57 | void OfflineAudioDestinationNode::initialize() |
58 | { |
59 | if (isInitialized()) |
60 | return; |
61 | |
62 | AudioNode::initialize(); |
63 | } |
64 | |
65 | void OfflineAudioDestinationNode::uninitialize() |
66 | { |
67 | if (!isInitialized()) |
68 | return; |
69 | |
70 | if (m_renderThread) { |
71 | m_renderThread->waitForCompletion(); |
72 | m_renderThread = nullptr; |
73 | } |
74 | |
75 | AudioNode::uninitialize(); |
76 | } |
77 | |
78 | void OfflineAudioDestinationNode::startRendering() |
79 | { |
80 | ALWAYS_LOG(LOGIDENTIFIER); |
81 | |
82 | ASSERT(isMainThread()); |
83 | ASSERT(m_renderTarget.get()); |
84 | if (!m_renderTarget.get()) |
85 | return; |
86 | |
87 | if (m_startedRendering) |
88 | return; |
89 | |
90 | m_startedRendering = true; |
91 | ref(); |
92 | // FIXME: Should we call lazyInitialize here? |
93 | // FIXME: We should probably limit the number of threads we create for offline audio. |
94 | m_renderThread = Thread::create("offline renderer" , [this] { |
95 | bool didRender = offlineRender(); |
96 | callOnMainThread([this, didRender] { |
97 | if (didRender) |
98 | context().fireCompletionEvent(); |
99 | deref(); |
100 | }); |
101 | }); |
102 | } |
103 | |
104 | bool OfflineAudioDestinationNode::offlineRender() |
105 | { |
106 | ASSERT(!isMainThread()); |
107 | ASSERT(m_renderBus.get()); |
108 | if (!m_renderBus.get()) |
109 | return false; |
110 | |
111 | bool isAudioContextInitialized = context().isInitialized(); |
112 | // FIXME: We used to assert that isAudioContextInitialized is true here. |
113 | // But it's trivially false in imported/w3c/web-platform-tests/webaudio/the-audio-api/the-offlineaudiocontext-interface/current-time-block-size.html |
114 | if (!isAudioContextInitialized) |
115 | return false; |
116 | |
117 | bool channelsMatch = m_renderBus->numberOfChannels() == m_renderTarget->numberOfChannels(); |
118 | ASSERT(channelsMatch); |
119 | if (!channelsMatch) |
120 | return false; |
121 | |
122 | bool isRenderBusAllocated = m_renderBus->length() >= renderQuantumSize; |
123 | ASSERT(isRenderBusAllocated); |
124 | if (!isRenderBusAllocated) |
125 | return false; |
126 | |
127 | // Break up the render target into smaller "render quantize" sized pieces. |
128 | // Render until we're finished. |
129 | size_t framesToProcess = m_renderTarget->length(); |
130 | unsigned numberOfChannels = m_renderTarget->numberOfChannels(); |
131 | |
132 | unsigned n = 0; |
133 | while (framesToProcess > 0) { |
134 | // Render one render quantum. |
135 | render(0, m_renderBus.get(), renderQuantumSize); |
136 | |
137 | size_t framesAvailableToCopy = std::min(framesToProcess, renderQuantumSize); |
138 | |
139 | for (unsigned channelIndex = 0; channelIndex < numberOfChannels; ++channelIndex) { |
140 | const float* source = m_renderBus->channel(channelIndex)->data(); |
141 | float* destination = m_renderTarget->channelData(channelIndex)->data(); |
142 | memcpy(destination + n, source, sizeof(float) * framesAvailableToCopy); |
143 | } |
144 | |
145 | n += framesAvailableToCopy; |
146 | framesToProcess -= framesAvailableToCopy; |
147 | } |
148 | |
149 | return true; |
150 | } |
151 | |
152 | } // namespace WebCore |
153 | |
154 | #endif // ENABLE(WEB_AUDIO) |
155 | |