| 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/AudioContext.h" | 30 #include "modules/webaudio/AudioContext.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 23 matching lines...) Expand all  Loading... | 
| 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 | 
| 84 void OfflineAudioDestinationHandler::startRendering() | 88 void OfflineAudioDestinationHandler::startRendering() | 
| 85 { | 89 { | 
| 86     ASSERT(isMainThread()); | 90     ASSERT(isMainThread()); | 
| 87     ASSERT(m_renderTarget); | 91     ASSERT(m_renderTarget); | 
|  | 92     ASSERT(m_renderThread); | 
|  | 93 | 
| 88     if (!m_renderTarget) | 94     if (!m_renderTarget) | 
| 89         return; | 95         return; | 
| 90 | 96 | 
| 91     if (!m_startedRendering) { | 97     // Rendering was not started. Starting now. | 
| 92         m_startedRendering = true; | 98     if (!m_isRenderingStarted) { | 
| 93         m_renderThread = adoptPtr(Platform::current()->createThread("Offline Aud
     io Renderer")); | 99         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)))); | 100         m_isRenderingStarted = true; | 
|  | 101         return; | 
| 95     } | 102     } | 
|  | 103 | 
|  | 104     // Rendering is already started, which implicitly means we resume the | 
|  | 105     // rendering by calling |runOfflineRendering| on m_renderThread. | 
|  | 106     m_renderThread->postTask(FROM_HERE, threadSafeBind(&OfflineAudioDestinationH
     andler::runOfflineRendering, this)); | 
| 96 } | 107 } | 
| 97 | 108 | 
| 98 void OfflineAudioDestinationHandler::stopRendering() | 109 void OfflineAudioDestinationHandler::stopRendering() | 
| 99 { | 110 { | 
| 100     ASSERT_NOT_REACHED(); | 111     ASSERT_NOT_REACHED(); | 
| 101 } | 112 } | 
| 102 | 113 | 
| 103 void OfflineAudioDestinationHandler::offlineRender() | 114 size_t OfflineAudioDestinationHandler::quantizeTimeToRenderQuantum(double when) 
     const | 
| 104 { | 115 { | 
| 105     offlineRenderInternal(); | 116     ASSERT(when >= 0); | 
| 106     context()->handlePostRenderTasks(); | 117 | 
|  | 118     size_t whenAsFrame = when * sampleRate(); | 
|  | 119     return whenAsFrame - (whenAsFrame % renderQuantumSize); | 
| 107 } | 120 } | 
| 108 | 121 | 
| 109 void OfflineAudioDestinationHandler::offlineRenderInternal() | 122 WebThread* OfflineAudioDestinationHandler::offlineRenderThread() | 
|  | 123 { | 
|  | 124     ASSERT(context()->isOfflineContext()); | 
|  | 125     ASSERT(m_renderThread); | 
|  | 126 | 
|  | 127     return m_renderThread.get(); | 
|  | 128 } | 
|  | 129 | 
|  | 130 void OfflineAudioDestinationHandler::startOfflineRendering() | 
| 110 { | 131 { | 
| 111     ASSERT(!isMainThread()); | 132     ASSERT(!isMainThread()); | 
|  | 133     ASSERT(context()->isOfflineContext()); | 
|  | 134 | 
| 112     ASSERT(m_renderBus); | 135     ASSERT(m_renderBus); | 
| 113     if (!m_renderBus) | 136     if (!m_renderBus) | 
| 114         return; | 137         return; | 
| 115 | 138 | 
| 116     bool isAudioContextInitialized = context()->isInitialized(); | 139     bool isAudioContextInitialized = context()->isInitialized(); | 
| 117     ASSERT(isAudioContextInitialized); | 140     ASSERT(isAudioContextInitialized); | 
| 118     if (!isAudioContextInitialized) | 141     if (!isAudioContextInitialized) | 
| 119         return; | 142         return; | 
| 120 | 143 | 
| 121     bool channelsMatch = m_renderBus->numberOfChannels() == m_renderTarget->numb
     erOfChannels(); | 144     bool channelsMatch = m_renderBus->numberOfChannels() == m_renderTarget->numb
     erOfChannels(); | 
| 122     ASSERT(channelsMatch); | 145     ASSERT(channelsMatch); | 
| 123     if (!channelsMatch) | 146     if (!channelsMatch) | 
| 124         return; | 147         return; | 
| 125 | 148 | 
| 126     bool isRenderBusAllocated = m_renderBus->length() >= renderQuantumSize; | 149     bool isRenderBusAllocated = m_renderBus->length() >= renderQuantumSize; | 
| 127     ASSERT(isRenderBusAllocated); | 150     ASSERT(isRenderBusAllocated); | 
| 128     if (!isRenderBusAllocated) | 151     if (!isRenderBusAllocated) | 
| 129         return; | 152         return; | 
| 130 | 153 | 
| 131     // Break up the render target into smaller "render quantize" sized pieces. | 154     m_framesToProcess = m_renderTarget->length(); | 
| 132     // Render until we're finished. | 155 | 
| 133     size_t framesToProcess = m_renderTarget->length(); | 156     // Start rendering. | 
|  | 157     runOfflineRendering(); | 
|  | 158 } | 
|  | 159 | 
|  | 160 void OfflineAudioDestinationHandler::runOfflineRendering() | 
|  | 161 { | 
|  | 162     ASSERT(!isMainThread()); | 
|  | 163     ASSERT(context()->isOfflineContext()); | 
|  | 164 | 
| 134     unsigned numberOfChannels = m_renderTarget->numberOfChannels(); | 165     unsigned numberOfChannels = m_renderTarget->numberOfChannels(); | 
| 135 | 166 | 
| 136     unsigned n = 0; | 167     // If there is more to process and there is no suspension at the moment, | 
| 137     while (framesToProcess > 0) { | 168     // do continue to render quanta. If there is a suspend scheduled at the | 
| 138         // Render one render quantum. | 169     // current sample frame, stop the render loop and put the context into the | 
|  | 170     // suspended state. Then calling OfflineAudioContext.resume() will pick up | 
|  | 171     // the render loop again from where it was suspended. | 
|  | 172     while (m_framesToProcess > 0 && !context()->shouldSuspendNow()) { | 
|  | 173 | 
|  | 174         // Render one render quantum. Note that this includes pre/post render | 
|  | 175         // tasks from the online audio context. | 
| 139         render(0, m_renderBus.get(), renderQuantumSize); | 176         render(0, m_renderBus.get(), renderQuantumSize); | 
| 140 | 177 | 
| 141         size_t framesAvailableToCopy = std::min(framesToProcess, renderQuantumSi
     ze); | 178         size_t framesAvailableToCopy = std::min(m_framesToProcess, renderQuantum
     Size); | 
| 142 | 179 | 
| 143         for (unsigned channelIndex = 0; channelIndex < numberOfChannels; ++chann
     elIndex) { | 180         for (unsigned channelIndex = 0; channelIndex < numberOfChannels; ++chann
     elIndex) { | 
| 144             const float* source = m_renderBus->channel(channelIndex)->data(); | 181             const float* source = m_renderBus->channel(channelIndex)->data(); | 
| 145             float* destination = m_renderTarget->getChannelData(channelIndex)->d
     ata(); | 182             float* destination = m_renderTarget->getChannelData(channelIndex)->d
     ata(); | 
| 146             memcpy(destination + n, source, sizeof(float) * framesAvailableToCop
     y); | 183             memcpy(destination + m_framesProcessed, source, sizeof(float) * fram
     esAvailableToCopy); | 
| 147         } | 184         } | 
| 148 | 185 | 
| 149         n += framesAvailableToCopy; | 186         m_framesProcessed += framesAvailableToCopy; | 
| 150         framesToProcess -= framesAvailableToCopy; | 187         m_framesToProcess -= framesAvailableToCopy; | 
| 151     } | 188     } | 
| 152 | 189 | 
|  | 190     // Finish up the rendering loop if there is no more to process. | 
|  | 191     if (m_framesToProcess <= 0) { | 
|  | 192         ASSERT(m_framesToProcess == 0); | 
|  | 193         finishOfflineRendering(); | 
|  | 194         return; | 
|  | 195     } | 
|  | 196 | 
|  | 197     // Otherwise resolve pending suspend promises. | 
|  | 198     context()->resolvePendingSuspendPromises(); | 
|  | 199 } | 
|  | 200 | 
|  | 201 void OfflineAudioDestinationHandler::finishOfflineRendering() | 
|  | 202 { | 
|  | 203     ASSERT(!isMainThread()); | 
|  | 204     ASSERT(context()->isOfflineContext()); | 
|  | 205 | 
| 153     // Our work is done. Let the AudioContext know. | 206     // Our work is done. Let the AudioContext know. | 
| 154     if (context()->executionContext()) | 207     if (context()->executionContext()) | 
| 155         context()->executionContext()->postTask(FROM_HERE, createCrossThreadTask
     (&OfflineAudioDestinationHandler::notifyComplete, PassRefPtr<OfflineAudioDestina
     tionHandler>(this))); | 208         context()->executionContext()->postTask(FROM_HERE, createCrossThreadTask
     (&OfflineAudioDestinationHandler::notifyComplete, this)); | 
| 156 } | 209 } | 
| 157 | 210 | 
| 158 void OfflineAudioDestinationHandler::notifyComplete() | 211 void OfflineAudioDestinationHandler::notifyComplete() | 
| 159 { | 212 { | 
| 160     // The AudioContext might be gone. | 213     // The OfflineAudioContext might be gone. | 
| 161     if (context()) | 214     if (context()) | 
| 162         context()->fireCompletionEvent(); | 215         context()->fireCompletionEvent(); | 
| 163 } | 216 } | 
| 164 | 217 | 
| 165 // ---------------------------------------------------------------- | 218 // ---------------------------------------------------------------- | 
| 166 | 219 | 
| 167 OfflineAudioDestinationNode::OfflineAudioDestinationNode(AudioContext& context, 
     AudioBuffer* renderTarget) | 220 OfflineAudioDestinationNode::OfflineAudioDestinationNode(AudioContext& context, 
     AudioBuffer* renderTarget) | 
| 168     : AudioDestinationNode(context) | 221     : AudioDestinationNode(context) | 
| 169 { | 222 { | 
| 170     setHandler(OfflineAudioDestinationHandler::create(*this, renderTarget)); | 223     setHandler(OfflineAudioDestinationHandler::create(*this, renderTarget)); | 
| 171 } | 224 } | 
| 172 | 225 | 
| 173 OfflineAudioDestinationNode* OfflineAudioDestinationNode::create(AudioContext* c
     ontext, AudioBuffer* renderTarget) | 226 OfflineAudioDestinationNode* OfflineAudioDestinationNode::create(AudioContext* c
     ontext, AudioBuffer* renderTarget) | 
| 174 { | 227 { | 
| 175     return new OfflineAudioDestinationNode(*context, renderTarget); | 228     return new OfflineAudioDestinationNode(*context, renderTarget); | 
| 176 } | 229 } | 
| 177 | 230 | 
| 178 } // namespace blink | 231 } // namespace blink | 
| 179 | 232 | 
| 180 #endif // ENABLE(WEB_AUDIO) | 233 #endif // ENABLE(WEB_AUDIO) | 
| OLD | NEW | 
|---|