| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2011, Google Inc. All rights reserved. | 2 * Copyright (C) 2011, 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 10 matching lines...) Expand all Loading... |
| 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 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. | 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 23 */ | 23 */ |
| 24 | 24 |
| 25 #include "config.h" | 25 #include "config.h" |
| 26 #if ENABLE(WEB_AUDIO) | 26 #if ENABLE(WEB_AUDIO) |
| 27 #include "modules/webaudio/OfflineAudioDestinationNode.h" | 27 #include "modules/webaudio/OfflineAudioDestinationNode.h" |
| 28 | 28 |
| 29 #include "core/dom/CrossThreadTask.h" | 29 #include "core/dom/CrossThreadTask.h" |
| 30 #include "modules/webaudio/AbstractAudioContext.h" | 30 #include "modules/webaudio/AbstractAudioContext.h" |
| 31 #include "modules/webaudio/OfflineAudioContext.h" | |
| 32 #include "platform/Task.h" | 31 #include "platform/Task.h" |
| 33 #include "platform/audio/AudioBus.h" | 32 #include "platform/audio/AudioBus.h" |
| 34 #include "platform/audio/HRTFDatabaseLoader.h" | 33 #include "platform/audio/HRTFDatabaseLoader.h" |
| 35 #include "public/platform/Platform.h" | 34 #include "public/platform/Platform.h" |
| 36 #include <algorithm> | 35 #include <algorithm> |
| 37 | 36 |
| 38 namespace blink { | 37 namespace blink { |
| 39 | 38 |
| 40 const size_t renderQuantumSize = 128; | 39 const size_t renderQuantumSize = 128; |
| 41 | 40 |
| 42 OfflineAudioDestinationHandler::OfflineAudioDestinationHandler(AudioNode& node,
AudioBuffer* renderTarget) | 41 OfflineAudioDestinationHandler::OfflineAudioDestinationHandler(AudioNode& node,
AudioBuffer* renderTarget) |
| 43 : AudioDestinationHandler(node, renderTarget->sampleRate()) | 42 : AudioDestinationHandler(node, renderTarget->sampleRate()) |
| 44 , m_renderTarget(renderTarget) | 43 , m_renderTarget(renderTarget) |
| 45 , m_renderThread(adoptPtr(Platform::current()->createThread("offline audio r
enderer"))) | 44 , m_startedRendering(false) |
| 46 , m_framesProcessed(0) | |
| 47 , m_framesToProcess(0) | |
| 48 , m_isRenderingStarted(false) | |
| 49 { | 45 { |
| 50 m_renderBus = AudioBus::create(renderTarget->numberOfChannels(), renderQuant
umSize); | 46 m_renderBus = AudioBus::create(renderTarget->numberOfChannels(), renderQuant
umSize); |
| 51 } | 47 } |
| 52 | 48 |
| 53 PassRefPtr<OfflineAudioDestinationHandler> OfflineAudioDestinationHandler::creat
e(AudioNode& node, AudioBuffer* renderTarget) | 49 PassRefPtr<OfflineAudioDestinationHandler> OfflineAudioDestinationHandler::creat
e(AudioNode& node, AudioBuffer* renderTarget) |
| 54 { | 50 { |
| 55 return adoptRef(new OfflineAudioDestinationHandler(node, renderTarget)); | 51 return adoptRef(new OfflineAudioDestinationHandler(node, renderTarget)); |
| 56 } | 52 } |
| 57 | 53 |
| 58 OfflineAudioDestinationHandler::~OfflineAudioDestinationHandler() | 54 OfflineAudioDestinationHandler::~OfflineAudioDestinationHandler() |
| (...skipping 19 matching lines...) Expand all Loading... |
| 78 { | 74 { |
| 79 if (!isInitialized()) | 75 if (!isInitialized()) |
| 80 return; | 76 return; |
| 81 | 77 |
| 82 if (m_renderThread) | 78 if (m_renderThread) |
| 83 m_renderThread.clear(); | 79 m_renderThread.clear(); |
| 84 | 80 |
| 85 AudioHandler::uninitialize(); | 81 AudioHandler::uninitialize(); |
| 86 } | 82 } |
| 87 | 83 |
| 88 OfflineAudioContext* OfflineAudioDestinationHandler::context() const | |
| 89 { | |
| 90 return static_cast<OfflineAudioContext*>(m_context); | |
| 91 } | |
| 92 | |
| 93 void OfflineAudioDestinationHandler::startRendering() | 84 void OfflineAudioDestinationHandler::startRendering() |
| 94 { | 85 { |
| 95 ASSERT(isMainThread()); | 86 ASSERT(isMainThread()); |
| 96 ASSERT(m_renderTarget); | 87 ASSERT(m_renderTarget); |
| 97 ASSERT(m_renderThread); | |
| 98 | |
| 99 if (!m_renderTarget) | 88 if (!m_renderTarget) |
| 100 return; | 89 return; |
| 101 | 90 |
| 102 // Rendering was not started. Starting now. | 91 if (!m_startedRendering) { |
| 103 if (!m_isRenderingStarted) { | 92 m_startedRendering = true; |
| 104 m_renderThread->postTask(FROM_HERE, new Task(threadSafeBind(&OfflineAudi
oDestinationHandler::startOfflineRendering, this))); | 93 m_renderThread = adoptPtr(Platform::current()->createThread("Offline Aud
io Renderer")); |
| 105 m_isRenderingStarted = true; | 94 m_renderThread->postTask(FROM_HERE, new Task(threadSafeBind(&OfflineAudi
oDestinationHandler::offlineRender, PassRefPtr<OfflineAudioDestinationHandler>(t
his)))); |
| 106 return; | |
| 107 } | 95 } |
| 108 | |
| 109 // Rendering is already started, which implicitly means we resume the | |
| 110 // rendering by calling |runOfflineRendering| on m_renderThread. | |
| 111 m_renderThread->postTask(FROM_HERE, threadSafeBind(&OfflineAudioDestinationH
andler::runOfflineRendering, this)); | |
| 112 } | 96 } |
| 113 | 97 |
| 114 void OfflineAudioDestinationHandler::stopRendering() | 98 void OfflineAudioDestinationHandler::stopRendering() |
| 115 { | 99 { |
| 116 ASSERT_NOT_REACHED(); | 100 ASSERT_NOT_REACHED(); |
| 117 } | 101 } |
| 118 | 102 |
| 119 size_t OfflineAudioDestinationHandler::quantizeTimeToRenderQuantum(double when)
const | 103 void OfflineAudioDestinationHandler::offlineRender() |
| 120 { | 104 { |
| 121 ASSERT(when >= 0); | 105 offlineRenderInternal(); |
| 122 | 106 context()->handlePostRenderTasks(); |
| 123 size_t whenAsFrame = when * sampleRate(); | |
| 124 return whenAsFrame - (whenAsFrame % renderQuantumSize); | |
| 125 } | 107 } |
| 126 | 108 |
| 127 WebThread* OfflineAudioDestinationHandler::offlineRenderThread() | 109 void OfflineAudioDestinationHandler::offlineRenderInternal() |
| 128 { | |
| 129 ASSERT(m_renderThread); | |
| 130 | |
| 131 return m_renderThread.get(); | |
| 132 } | |
| 133 | |
| 134 void OfflineAudioDestinationHandler::startOfflineRendering() | |
| 135 { | 110 { |
| 136 ASSERT(!isMainThread()); | 111 ASSERT(!isMainThread()); |
| 137 | |
| 138 ASSERT(m_renderBus); | 112 ASSERT(m_renderBus); |
| 139 if (!m_renderBus) | 113 if (!m_renderBus) |
| 140 return; | 114 return; |
| 141 | 115 |
| 142 bool isAudioContextInitialized = context()->isInitialized(); | 116 bool isAudioContextInitialized = context()->isInitialized(); |
| 143 ASSERT(isAudioContextInitialized); | 117 ASSERT(isAudioContextInitialized); |
| 144 if (!isAudioContextInitialized) | 118 if (!isAudioContextInitialized) |
| 145 return; | 119 return; |
| 146 | 120 |
| 147 bool channelsMatch = m_renderBus->numberOfChannels() == m_renderTarget->numb
erOfChannels(); | 121 bool channelsMatch = m_renderBus->numberOfChannels() == m_renderTarget->numb
erOfChannels(); |
| 148 ASSERT(channelsMatch); | 122 ASSERT(channelsMatch); |
| 149 if (!channelsMatch) | 123 if (!channelsMatch) |
| 150 return; | 124 return; |
| 151 | 125 |
| 152 bool isRenderBusAllocated = m_renderBus->length() >= renderQuantumSize; | 126 bool isRenderBusAllocated = m_renderBus->length() >= renderQuantumSize; |
| 153 ASSERT(isRenderBusAllocated); | 127 ASSERT(isRenderBusAllocated); |
| 154 if (!isRenderBusAllocated) | 128 if (!isRenderBusAllocated) |
| 155 return; | 129 return; |
| 156 | 130 |
| 157 m_framesToProcess = m_renderTarget->length(); | 131 // Break up the render target into smaller "render quantize" sized pieces. |
| 158 | 132 // Render until we're finished. |
| 159 // Start rendering. | 133 size_t framesToProcess = m_renderTarget->length(); |
| 160 runOfflineRendering(); | |
| 161 } | |
| 162 | |
| 163 void OfflineAudioDestinationHandler::runOfflineRendering() | |
| 164 { | |
| 165 ASSERT(!isMainThread()); | |
| 166 | |
| 167 unsigned numberOfChannels = m_renderTarget->numberOfChannels(); | 134 unsigned numberOfChannels = m_renderTarget->numberOfChannels(); |
| 168 | 135 |
| 169 // If there is more to process and there is no suspension at the moment, | 136 unsigned n = 0; |
| 170 // do continue to render quanta. If there is a suspend scheduled at the | 137 while (framesToProcess > 0) { |
| 171 // current sample frame, stop the render loop and put the context into the | 138 // Render one render quantum. |
| 172 // suspended state. Then calling OfflineAudioContext.resume() will pick up | |
| 173 // the render loop again from where it was suspended. | |
| 174 while (m_framesToProcess > 0 && !context()->shouldSuspendNow()) { | |
| 175 | |
| 176 // Render one render quantum. Note that this includes pre/post render | |
| 177 // tasks from the online audio context. | |
| 178 render(0, m_renderBus.get(), renderQuantumSize); | 139 render(0, m_renderBus.get(), renderQuantumSize); |
| 179 | 140 |
| 180 size_t framesAvailableToCopy = std::min(m_framesToProcess, renderQuantum
Size); | 141 size_t framesAvailableToCopy = std::min(framesToProcess, renderQuantumSi
ze); |
| 181 | 142 |
| 182 for (unsigned channelIndex = 0; channelIndex < numberOfChannels; ++chann
elIndex) { | 143 for (unsigned channelIndex = 0; channelIndex < numberOfChannels; ++chann
elIndex) { |
| 183 const float* source = m_renderBus->channel(channelIndex)->data(); | 144 const float* source = m_renderBus->channel(channelIndex)->data(); |
| 184 float* destination = m_renderTarget->getChannelData(channelIndex)->d
ata(); | 145 float* destination = m_renderTarget->getChannelData(channelIndex)->d
ata(); |
| 185 memcpy(destination + m_framesProcessed, source, sizeof(float) * fram
esAvailableToCopy); | 146 memcpy(destination + n, source, sizeof(float) * framesAvailableToCop
y); |
| 186 } | 147 } |
| 187 | 148 |
| 188 m_framesProcessed += framesAvailableToCopy; | 149 n += framesAvailableToCopy; |
| 189 m_framesToProcess -= framesAvailableToCopy; | 150 framesToProcess -= framesAvailableToCopy; |
| 190 } | 151 } |
| 191 | 152 |
| 192 // Finish up the rendering loop if there is no more to process. | |
| 193 if (m_framesToProcess <= 0) { | |
| 194 ASSERT(m_framesToProcess == 0); | |
| 195 finishOfflineRendering(); | |
| 196 return; | |
| 197 } | |
| 198 | |
| 199 // Otherwise resolve pending suspend promises. | |
| 200 context()->resolvePendingSuspendPromises(); | |
| 201 } | |
| 202 | |
| 203 void OfflineAudioDestinationHandler::finishOfflineRendering() | |
| 204 { | |
| 205 ASSERT(!isMainThread()); | |
| 206 | |
| 207 // Our work is done. Let the AbstractAudioContext know. | 153 // Our work is done. Let the AbstractAudioContext know. |
| 208 if (context()->executionContext()) | 154 if (context()->executionContext()) |
| 209 context()->executionContext()->postTask(FROM_HERE, createCrossThreadTask
(&OfflineAudioDestinationHandler::notifyComplete, this)); | 155 context()->executionContext()->postTask(FROM_HERE, createCrossThreadTask
(&OfflineAudioDestinationHandler::notifyComplete, PassRefPtr<OfflineAudioDestina
tionHandler>(this))); |
| 210 } | 156 } |
| 211 | 157 |
| 212 void OfflineAudioDestinationHandler::notifyComplete() | 158 void OfflineAudioDestinationHandler::notifyComplete() |
| 213 { | 159 { |
| 214 // The AbstractAudioContext might be gone. | 160 // The AbstractAudioContext might be gone. |
| 215 if (context()) | 161 if (context()) |
| 216 context()->fireCompletionEvent(); | 162 context()->fireCompletionEvent(); |
| 217 } | 163 } |
| 218 | 164 |
| 219 // ---------------------------------------------------------------- | 165 // ---------------------------------------------------------------- |
| 220 | 166 |
| 221 OfflineAudioDestinationNode::OfflineAudioDestinationNode(AbstractAudioContext& c
ontext, AudioBuffer* renderTarget) | 167 OfflineAudioDestinationNode::OfflineAudioDestinationNode(AbstractAudioContext& c
ontext, AudioBuffer* renderTarget) |
| 222 : AudioDestinationNode(context) | 168 : AudioDestinationNode(context) |
| 223 { | 169 { |
| 224 setHandler(OfflineAudioDestinationHandler::create(*this, renderTarget)); | 170 setHandler(OfflineAudioDestinationHandler::create(*this, renderTarget)); |
| 225 } | 171 } |
| 226 | 172 |
| 227 OfflineAudioDestinationNode* OfflineAudioDestinationNode::create(AbstractAudioCo
ntext* context, AudioBuffer* renderTarget) | 173 OfflineAudioDestinationNode* OfflineAudioDestinationNode::create(AbstractAudioCo
ntext* context, AudioBuffer* renderTarget) |
| 228 { | 174 { |
| 229 return new OfflineAudioDestinationNode(*context, renderTarget); | 175 return new OfflineAudioDestinationNode(*context, renderTarget); |
| 230 } | 176 } |
| 231 | 177 |
| 232 } // namespace blink | 178 } // namespace blink |
| 233 | 179 |
| 234 #endif // ENABLE(WEB_AUDIO) | 180 #endif // ENABLE(WEB_AUDIO) |
| OLD | NEW |