| 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 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 57 m_channelInterpretation = AudioBus::Speakers; | 57 m_channelInterpretation = AudioBus::Speakers; |
| 58 } | 58 } |
| 59 | 59 |
| 60 PassRefPtr<OfflineAudioDestinationHandler> OfflineAudioDestinationHandler::creat
e(AudioNode& node, AudioBuffer* renderTarget) | 60 PassRefPtr<OfflineAudioDestinationHandler> OfflineAudioDestinationHandler::creat
e(AudioNode& node, AudioBuffer* renderTarget) |
| 61 { | 61 { |
| 62 return adoptRef(new OfflineAudioDestinationHandler(node, renderTarget)); | 62 return adoptRef(new OfflineAudioDestinationHandler(node, renderTarget)); |
| 63 } | 63 } |
| 64 | 64 |
| 65 OfflineAudioDestinationHandler::~OfflineAudioDestinationHandler() | 65 OfflineAudioDestinationHandler::~OfflineAudioDestinationHandler() |
| 66 { | 66 { |
| 67 ASSERT(!isInitialized()); | 67 DCHECK(!isInitialized()); |
| 68 } | 68 } |
| 69 | 69 |
| 70 void OfflineAudioDestinationHandler::dispose() | 70 void OfflineAudioDestinationHandler::dispose() |
| 71 { | 71 { |
| 72 uninitialize(); | 72 uninitialize(); |
| 73 AudioDestinationHandler::dispose(); | 73 AudioDestinationHandler::dispose(); |
| 74 } | 74 } |
| 75 | 75 |
| 76 void OfflineAudioDestinationHandler::initialize() | 76 void OfflineAudioDestinationHandler::initialize() |
| 77 { | 77 { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 97 return static_cast<OfflineAudioContext*>(AudioDestinationHandler::context())
; | 97 return static_cast<OfflineAudioContext*>(AudioDestinationHandler::context())
; |
| 98 } | 98 } |
| 99 | 99 |
| 100 unsigned long OfflineAudioDestinationHandler::maxChannelCount() const | 100 unsigned long OfflineAudioDestinationHandler::maxChannelCount() const |
| 101 { | 101 { |
| 102 return m_channelCount; | 102 return m_channelCount; |
| 103 } | 103 } |
| 104 | 104 |
| 105 void OfflineAudioDestinationHandler::startRendering() | 105 void OfflineAudioDestinationHandler::startRendering() |
| 106 { | 106 { |
| 107 ASSERT(isMainThread()); | 107 DCHECK(isMainThread()); |
| 108 ASSERT(m_renderThread); | 108 DCHECK(m_renderThread); |
| 109 ASSERT(m_renderTarget); | 109 DCHECK(m_renderTarget); |
| 110 | 110 |
| 111 if (!m_renderTarget) | 111 if (!m_renderTarget) |
| 112 return; | 112 return; |
| 113 | 113 |
| 114 // Rendering was not started. Starting now. | 114 // Rendering was not started. Starting now. |
| 115 if (!m_isRenderingStarted) { | 115 if (!m_isRenderingStarted) { |
| 116 m_isRenderingStarted = true; | 116 m_isRenderingStarted = true; |
| 117 m_renderThread->getWebTaskRunner()->postTask(BLINK_FROM_HERE, | 117 m_renderThread->getWebTaskRunner()->postTask(BLINK_FROM_HERE, |
| 118 crossThreadBind(&OfflineAudioDestinationHandler::startOfflineRenderi
ng, wrapPassRefPtr(this))); | 118 crossThreadBind(&OfflineAudioDestinationHandler::startOfflineRenderi
ng, wrapPassRefPtr(this))); |
| 119 return; | 119 return; |
| 120 } | 120 } |
| 121 | 121 |
| 122 // Rendering is already started, which implicitly means we resume the | 122 // Rendering is already started, which implicitly means we resume the |
| 123 // rendering by calling |doOfflineRendering| on the render thread. | 123 // rendering by calling |doOfflineRendering| on the render thread. |
| 124 m_renderThread->getWebTaskRunner()->postTask(BLINK_FROM_HERE, | 124 m_renderThread->getWebTaskRunner()->postTask(BLINK_FROM_HERE, |
| 125 crossThreadBind(&OfflineAudioDestinationHandler::doOfflineRendering, wra
pPassRefPtr(this))); | 125 crossThreadBind(&OfflineAudioDestinationHandler::doOfflineRendering, wra
pPassRefPtr(this))); |
| 126 } | 126 } |
| 127 | 127 |
| 128 void OfflineAudioDestinationHandler::stopRendering() | 128 void OfflineAudioDestinationHandler::stopRendering() |
| 129 { | 129 { |
| 130 // offline audio rendering CANNOT BE stopped by JavaScript. | 130 // offline audio rendering CANNOT BE stopped by JavaScript. |
| 131 ASSERT_NOT_REACHED(); | 131 ASSERT_NOT_REACHED(); |
| 132 } | 132 } |
| 133 | 133 |
| 134 WebThread* OfflineAudioDestinationHandler::offlineRenderThread() | 134 WebThread* OfflineAudioDestinationHandler::offlineRenderThread() |
| 135 { | 135 { |
| 136 ASSERT(m_renderThread); | 136 DCHECK(m_renderThread); |
| 137 | 137 |
| 138 return m_renderThread.get(); | 138 return m_renderThread.get(); |
| 139 } | 139 } |
| 140 | 140 |
| 141 void OfflineAudioDestinationHandler::startOfflineRendering() | 141 void OfflineAudioDestinationHandler::startOfflineRendering() |
| 142 { | 142 { |
| 143 ASSERT(!isMainThread()); | 143 DCHECK(!isMainThread()); |
| 144 | 144 |
| 145 ASSERT(m_renderBus); | 145 DCHECK(m_renderBus); |
| 146 if (!m_renderBus) | 146 if (!m_renderBus) |
| 147 return; | 147 return; |
| 148 | 148 |
| 149 bool isAudioContextInitialized = context()->isDestinationInitialized(); | 149 bool isAudioContextInitialized = context()->isDestinationInitialized(); |
| 150 ASSERT(isAudioContextInitialized); | 150 DCHECK(isAudioContextInitialized); |
| 151 if (!isAudioContextInitialized) | 151 if (!isAudioContextInitialized) |
| 152 return; | 152 return; |
| 153 | 153 |
| 154 bool channelsMatch = m_renderBus->numberOfChannels() == m_renderTarget->numb
erOfChannels(); | 154 bool channelsMatch = m_renderBus->numberOfChannels() == m_renderTarget->numb
erOfChannels(); |
| 155 ASSERT(channelsMatch); | 155 DCHECK(channelsMatch); |
| 156 if (!channelsMatch) | 156 if (!channelsMatch) |
| 157 return; | 157 return; |
| 158 | 158 |
| 159 bool isRenderBusAllocated = m_renderBus->length() >= renderQuantumSize; | 159 bool isRenderBusAllocated = m_renderBus->length() >= renderQuantumSize; |
| 160 ASSERT(isRenderBusAllocated); | 160 DCHECK(isRenderBusAllocated); |
| 161 if (!isRenderBusAllocated) | 161 if (!isRenderBusAllocated) |
| 162 return; | 162 return; |
| 163 | 163 |
| 164 // Start rendering. | 164 // Start rendering. |
| 165 doOfflineRendering(); | 165 doOfflineRendering(); |
| 166 } | 166 } |
| 167 | 167 |
| 168 void OfflineAudioDestinationHandler::doOfflineRendering() | 168 void OfflineAudioDestinationHandler::doOfflineRendering() |
| 169 { | 169 { |
| 170 ASSERT(!isMainThread()); | 170 DCHECK(!isMainThread()); |
| 171 | 171 |
| 172 unsigned numberOfChannels = m_renderTarget->numberOfChannels(); | 172 unsigned numberOfChannels = m_renderTarget->numberOfChannels(); |
| 173 | 173 |
| 174 // Reset the suspend flag. | 174 // Reset the suspend flag. |
| 175 m_shouldSuspend = false; | 175 m_shouldSuspend = false; |
| 176 | 176 |
| 177 // If there is more to process and there is no suspension at the moment, | 177 // If there is more to process and there is no suspension at the moment, |
| 178 // do continue to render quanta. Then calling OfflineAudioContext.resume() w
ill pick up | 178 // do continue to render quanta. Then calling OfflineAudioContext.resume() w
ill pick up |
| 179 // the render loop again from where it was suspended. | 179 // the render loop again from where it was suspended. |
| 180 while (m_framesToProcess > 0 && !m_shouldSuspend) { | 180 while (m_framesToProcess > 0 && !m_shouldSuspend) { |
| 181 | 181 |
| 182 // Suspend the rendering and update m_shouldSuspend if a scheduled | 182 // Suspend the rendering and update m_shouldSuspend if a scheduled |
| 183 // suspend found at the current sample frame. Otherwise render one | 183 // suspend found at the current sample frame. Otherwise render one |
| 184 // quantum and return false. | 184 // quantum and return false. |
| 185 m_shouldSuspend = renderIfNotSuspended(0, m_renderBus.get(), renderQuant
umSize); | 185 m_shouldSuspend = renderIfNotSuspended(0, m_renderBus.get(), renderQuant
umSize); |
| 186 | 186 |
| 187 if (m_shouldSuspend) | 187 if (m_shouldSuspend) |
| 188 return; | 188 return; |
| 189 | 189 |
| 190 size_t framesAvailableToCopy = std::min(m_framesToProcess, renderQuantum
Size); | 190 size_t framesAvailableToCopy = std::min(m_framesToProcess, renderQuantum
Size); |
| 191 | 191 |
| 192 for (unsigned channelIndex = 0; channelIndex < numberOfChannels; ++chann
elIndex) { | 192 for (unsigned channelIndex = 0; channelIndex < numberOfChannels; ++chann
elIndex) { |
| 193 const float* source = m_renderBus->channel(channelIndex)->data(); | 193 const float* source = m_renderBus->channel(channelIndex)->data(); |
| 194 float* destination = m_renderTarget->getChannelData(channelIndex)->d
ata(); | 194 float* destination = m_renderTarget->getChannelData(channelIndex)->d
ata(); |
| 195 memcpy(destination + m_framesProcessed, source, sizeof(float) * fram
esAvailableToCopy); | 195 memcpy(destination + m_framesProcessed, source, sizeof(float) * fram
esAvailableToCopy); |
| 196 } | 196 } |
| 197 | 197 |
| 198 m_framesProcessed += framesAvailableToCopy; | 198 m_framesProcessed += framesAvailableToCopy; |
| 199 | 199 |
| 200 ASSERT(m_framesToProcess >= framesAvailableToCopy); | 200 DCHECK_GE(m_framesToProcess, framesAvailableToCopy); |
| 201 m_framesToProcess -= framesAvailableToCopy; | 201 m_framesToProcess -= framesAvailableToCopy; |
| 202 } | 202 } |
| 203 | 203 |
| 204 // Finish up the rendering loop if there is no more to process. | 204 // Finish up the rendering loop if there is no more to process. |
| 205 if (!m_framesToProcess) | 205 if (!m_framesToProcess) |
| 206 finishOfflineRendering(); | 206 finishOfflineRendering(); |
| 207 } | 207 } |
| 208 | 208 |
| 209 void OfflineAudioDestinationHandler::suspendOfflineRendering() | 209 void OfflineAudioDestinationHandler::suspendOfflineRendering() |
| 210 { | 210 { |
| 211 ASSERT(!isMainThread()); | 211 DCHECK(!isMainThread()); |
| 212 | 212 |
| 213 // The actual rendering has been suspended. Notify the context. | 213 // The actual rendering has been suspended. Notify the context. |
| 214 if (context()->getExecutionContext()) { | 214 if (context()->getExecutionContext()) { |
| 215 context()->getExecutionContext()->postTask( | 215 context()->getExecutionContext()->postTask( |
| 216 BLINK_FROM_HERE, | 216 BLINK_FROM_HERE, |
| 217 createCrossThreadTask(&OfflineAudioDestinationHandler::notifySuspend
, PassRefPtr<OfflineAudioDestinationHandler>(this), context()->currentSampleFram
e())); | 217 createCrossThreadTask(&OfflineAudioDestinationHandler::notifySuspend
, PassRefPtr<OfflineAudioDestinationHandler>(this), context()->currentSampleFram
e())); |
| 218 } | 218 } |
| 219 } | 219 } |
| 220 | 220 |
| 221 void OfflineAudioDestinationHandler::finishOfflineRendering() | 221 void OfflineAudioDestinationHandler::finishOfflineRendering() |
| 222 { | 222 { |
| 223 ASSERT(!isMainThread()); | 223 DCHECK(!isMainThread()); |
| 224 | 224 |
| 225 // The actual rendering has been completed. Notify the context. | 225 // The actual rendering has been completed. Notify the context. |
| 226 if (context()->getExecutionContext()) { | 226 if (context()->getExecutionContext()) { |
| 227 context()->getExecutionContext()->postTask(BLINK_FROM_HERE, | 227 context()->getExecutionContext()->postTask(BLINK_FROM_HERE, |
| 228 createCrossThreadTask(&OfflineAudioDestinationHandler::notifyComplet
e, PassRefPtr<OfflineAudioDestinationHandler>(this))); | 228 createCrossThreadTask(&OfflineAudioDestinationHandler::notifyComplet
e, PassRefPtr<OfflineAudioDestinationHandler>(this))); |
| 229 } | 229 } |
| 230 } | 230 } |
| 231 | 231 |
| 232 void OfflineAudioDestinationHandler::notifySuspend(size_t frame) | 232 void OfflineAudioDestinationHandler::notifySuspend(size_t frame) |
| 233 { | 233 { |
| 234 ASSERT(isMainThread()); | 234 DCHECK(isMainThread()); |
| 235 | 235 |
| 236 if (context()) | 236 if (context()) |
| 237 context()->resolveSuspendOnMainThread(frame); | 237 context()->resolveSuspendOnMainThread(frame); |
| 238 } | 238 } |
| 239 | 239 |
| 240 void OfflineAudioDestinationHandler::notifyComplete() | 240 void OfflineAudioDestinationHandler::notifyComplete() |
| 241 { | 241 { |
| 242 // The OfflineAudioContext might be gone. | 242 // The OfflineAudioContext might be gone. |
| 243 if (context()) | 243 if (context()) |
| 244 context()->fireCompletionEvent(); | 244 context()->fireCompletionEvent(); |
| 245 } | 245 } |
| 246 | 246 |
| 247 bool OfflineAudioDestinationHandler::renderIfNotSuspended(AudioBus* sourceBus, A
udioBus* destinationBus, size_t numberOfFrames) | 247 bool OfflineAudioDestinationHandler::renderIfNotSuspended(AudioBus* sourceBus, A
udioBus* destinationBus, size_t numberOfFrames) |
| 248 { | 248 { |
| 249 // We don't want denormals slowing down any of the audio processing | 249 // We don't want denormals slowing down any of the audio processing |
| 250 // since they can very seriously hurt performance. | 250 // since they can very seriously hurt performance. |
| 251 // This will take care of all AudioNodes because they all process within thi
s scope. | 251 // This will take care of all AudioNodes because they all process within thi
s scope. |
| 252 DenormalDisabler denormalDisabler; | 252 DenormalDisabler denormalDisabler; |
| 253 | 253 |
| 254 // Need to check if the context actually alive. Otherwise the subsequent | 254 // Need to check if the context actually alive. Otherwise the subsequent |
| 255 // steps will fail. If the context is not alive somehow, return immediately | 255 // steps will fail. If the context is not alive somehow, return immediately |
| 256 // and do nothing. | 256 // and do nothing. |
| 257 // | 257 // |
| 258 // TODO(hongchan): because the context can go away while rendering, so this | 258 // TODO(hongchan): because the context can go away while rendering, so this |
| 259 // check cannot guarantee the safe execution of the following steps. | 259 // check cannot guarantee the safe execution of the following steps. |
| 260 ASSERT(context()); | 260 DCHECK(context()); |
| 261 if (!context()) | 261 if (!context()) |
| 262 return false; | 262 return false; |
| 263 | 263 |
| 264 context()->deferredTaskHandler().setAudioThreadToCurrentThread(); | 264 context()->deferredTaskHandler().setAudioThreadToCurrentThread(); |
| 265 | 265 |
| 266 // If the destination node is not initialized, pass the silence to the final | 266 // If the destination node is not initialized, pass the silence to the final |
| 267 // audio destination (one step before the FIFO). This check is for the case | 267 // audio destination (one step before the FIFO). This check is for the case |
| 268 // where the destination is in the middle of tearing down process. | 268 // where the destination is in the middle of tearing down process. |
| 269 if (!isInitialized()) { | 269 if (!isInitialized()) { |
| 270 destinationBus->zero(); | 270 destinationBus->zero(); |
| 271 return false; | 271 return false; |
| 272 } | 272 } |
| 273 | 273 |
| 274 // Take care pre-render tasks at the beginning of each render quantum. Then | 274 // Take care pre-render tasks at the beginning of each render quantum. Then |
| 275 // it will stop the rendering loop if the context needs to be suspended | 275 // it will stop the rendering loop if the context needs to be suspended |
| 276 // at the beginning of the next render quantum. | 276 // at the beginning of the next render quantum. |
| 277 if (context()->handlePreOfflineRenderTasks()) { | 277 if (context()->handlePreOfflineRenderTasks()) { |
| 278 suspendOfflineRendering(); | 278 suspendOfflineRendering(); |
| 279 return true; | 279 return true; |
| 280 } | 280 } |
| 281 | 281 |
| 282 // Prepare the local audio input provider for this render quantum. | 282 // Prepare the local audio input provider for this render quantum. |
| 283 if (sourceBus) | 283 if (sourceBus) |
| 284 m_localAudioInputProvider.set(sourceBus); | 284 m_localAudioInputProvider.set(sourceBus); |
| 285 | 285 |
| 286 ASSERT(numberOfInputs() >= 1); | 286 DCHECK_GE(numberOfInputs(), 1u); |
| 287 if (numberOfInputs() < 1) { | 287 if (numberOfInputs() < 1) { |
| 288 destinationBus->zero(); | 288 destinationBus->zero(); |
| 289 return false; | 289 return false; |
| 290 } | 290 } |
| 291 // This will cause the node(s) connected to us to process, which in turn wil
l pull on their input(s), | 291 // This will cause the node(s) connected to us to process, which in turn wil
l pull on their input(s), |
| 292 // all the way backwards through the rendering graph. | 292 // all the way backwards through the rendering graph. |
| 293 AudioBus* renderedBus = input(0).pull(destinationBus, numberOfFrames); | 293 AudioBus* renderedBus = input(0).pull(destinationBus, numberOfFrames); |
| 294 | 294 |
| 295 if (!renderedBus) { | 295 if (!renderedBus) { |
| 296 destinationBus->zero(); | 296 destinationBus->zero(); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 319 { | 319 { |
| 320 setHandler(OfflineAudioDestinationHandler::create(*this, renderTarget)); | 320 setHandler(OfflineAudioDestinationHandler::create(*this, renderTarget)); |
| 321 } | 321 } |
| 322 | 322 |
| 323 OfflineAudioDestinationNode* OfflineAudioDestinationNode::create(BaseAudioContex
t* context, AudioBuffer* renderTarget) | 323 OfflineAudioDestinationNode* OfflineAudioDestinationNode::create(BaseAudioContex
t* context, AudioBuffer* renderTarget) |
| 324 { | 324 { |
| 325 return new OfflineAudioDestinationNode(*context, renderTarget); | 325 return new OfflineAudioDestinationNode(*context, renderTarget); |
| 326 } | 326 } |
| 327 | 327 |
| 328 } // namespace blink | 328 } // namespace blink |
| OLD | NEW |