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