| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010, Google Inc. All rights reserved. | 2 * Copyright (C) 2010, 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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 45 unsigned numberOfOutputChannels) | 45 unsigned numberOfOutputChannels) |
| 46 : AudioHandler(NodeTypeJavaScript, node, sampleRate), | 46 : AudioHandler(NodeTypeJavaScript, node, sampleRate), |
| 47 m_doubleBufferIndex(0), | 47 m_doubleBufferIndex(0), |
| 48 m_bufferSize(bufferSize), | 48 m_bufferSize(bufferSize), |
| 49 m_bufferReadWriteIndex(0), | 49 m_bufferReadWriteIndex(0), |
| 50 m_numberOfInputChannels(numberOfInputChannels), | 50 m_numberOfInputChannels(numberOfInputChannels), |
| 51 m_numberOfOutputChannels(numberOfOutputChannels), | 51 m_numberOfOutputChannels(numberOfOutputChannels), |
| 52 m_internalInputBus(AudioBus::create(numberOfInputChannels, | 52 m_internalInputBus(AudioBus::create(numberOfInputChannels, |
| 53 ProcessingSizeInFrames, | 53 ProcessingSizeInFrames, |
| 54 false)) { | 54 false)) { |
| 55 // Regardless of the allowed buffer sizes, we still need to process at the gra
nularity of the AudioNode. | 55 // Regardless of the allowed buffer sizes, we still need to process at the |
| 56 // granularity of the AudioNode. |
| 56 if (m_bufferSize < ProcessingSizeInFrames) | 57 if (m_bufferSize < ProcessingSizeInFrames) |
| 57 m_bufferSize = ProcessingSizeInFrames; | 58 m_bufferSize = ProcessingSizeInFrames; |
| 58 | 59 |
| 59 DCHECK_LE(numberOfInputChannels, BaseAudioContext::maxNumberOfChannels()); | 60 DCHECK_LE(numberOfInputChannels, BaseAudioContext::maxNumberOfChannels()); |
| 60 | 61 |
| 61 addInput(); | 62 addInput(); |
| 62 addOutput(numberOfOutputChannels); | 63 addOutput(numberOfOutputChannels); |
| 63 | 64 |
| 64 m_channelCount = numberOfInputChannels; | 65 m_channelCount = numberOfInputChannels; |
| 65 setInternalChannelCountMode(Explicit); | 66 setInternalChannelCountMode(Explicit); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 82 uninitialize(); | 83 uninitialize(); |
| 83 } | 84 } |
| 84 | 85 |
| 85 void ScriptProcessorHandler::initialize() { | 86 void ScriptProcessorHandler::initialize() { |
| 86 if (isInitialized()) | 87 if (isInitialized()) |
| 87 return; | 88 return; |
| 88 | 89 |
| 89 float sampleRate = context()->sampleRate(); | 90 float sampleRate = context()->sampleRate(); |
| 90 | 91 |
| 91 // Create double buffers on both the input and output sides. | 92 // Create double buffers on both the input and output sides. |
| 92 // These AudioBuffers will be directly accessed in the main thread by JavaScri
pt. | 93 // These AudioBuffers will be directly accessed in the main thread by |
| 94 // JavaScript. |
| 93 for (unsigned i = 0; i < 2; ++i) { | 95 for (unsigned i = 0; i < 2; ++i) { |
| 94 AudioBuffer* inputBuffer = | 96 AudioBuffer* inputBuffer = |
| 95 m_numberOfInputChannels ? AudioBuffer::create(m_numberOfInputChannels, | 97 m_numberOfInputChannels ? AudioBuffer::create(m_numberOfInputChannels, |
| 96 bufferSize(), sampleRate) | 98 bufferSize(), sampleRate) |
| 97 : 0; | 99 : 0; |
| 98 AudioBuffer* outputBuffer = | 100 AudioBuffer* outputBuffer = |
| 99 m_numberOfOutputChannels ? AudioBuffer::create(m_numberOfOutputChannels, | 101 m_numberOfOutputChannels ? AudioBuffer::create(m_numberOfOutputChannels, |
| 100 bufferSize(), sampleRate) | 102 bufferSize(), sampleRate) |
| 101 : 0; | 103 : 0; |
| 102 | 104 |
| 103 m_inputBuffers.append(inputBuffer); | 105 m_inputBuffers.append(inputBuffer); |
| 104 m_outputBuffers.append(outputBuffer); | 106 m_outputBuffers.append(outputBuffer); |
| 105 } | 107 } |
| 106 | 108 |
| 107 AudioHandler::initialize(); | 109 AudioHandler::initialize(); |
| 108 } | 110 } |
| 109 | 111 |
| 110 void ScriptProcessorHandler::process(size_t framesToProcess) { | 112 void ScriptProcessorHandler::process(size_t framesToProcess) { |
| 111 // Discussion about inputs and outputs: | 113 // Discussion about inputs and outputs: |
| 112 // As in other AudioNodes, ScriptProcessorNode uses an AudioBus for its input
and output (see inputBus and outputBus below). | 114 // As in other AudioNodes, ScriptProcessorNode uses an AudioBus for its input |
| 113 // Additionally, there is a double-buffering for input and output which is exp
osed directly to JavaScript (see inputBuffer and outputBuffer below). | 115 // and output (see inputBus and outputBus below). Additionally, there is a |
| 114 // This node is the producer for inputBuffer and the consumer for outputBuffer
. | 116 // double-buffering for input and output which is exposed directly to |
| 115 // The JavaScript code is the consumer of inputBuffer and the producer for out
putBuffer. | 117 // JavaScript (see inputBuffer and outputBuffer below). This node is the |
| 118 // producer for inputBuffer and the consumer for outputBuffer. The JavaScript |
| 119 // code is the consumer of inputBuffer and the producer for outputBuffer. |
| 116 | 120 |
| 117 // Get input and output busses. | 121 // Get input and output busses. |
| 118 AudioBus* inputBus = input(0).bus(); | 122 AudioBus* inputBus = input(0).bus(); |
| 119 AudioBus* outputBus = output(0).bus(); | 123 AudioBus* outputBus = output(0).bus(); |
| 120 | 124 |
| 121 // Get input and output buffers. We double-buffer both the input and output si
des. | 125 // Get input and output buffers. We double-buffer both the input and output |
| 126 // sides. |
| 122 unsigned doubleBufferIndex = this->doubleBufferIndex(); | 127 unsigned doubleBufferIndex = this->doubleBufferIndex(); |
| 123 bool isDoubleBufferIndexGood = doubleBufferIndex < 2 && | 128 bool isDoubleBufferIndexGood = doubleBufferIndex < 2 && |
| 124 doubleBufferIndex < m_inputBuffers.size() && | 129 doubleBufferIndex < m_inputBuffers.size() && |
| 125 doubleBufferIndex < m_outputBuffers.size(); | 130 doubleBufferIndex < m_outputBuffers.size(); |
| 126 DCHECK(isDoubleBufferIndexGood); | 131 DCHECK(isDoubleBufferIndexGood); |
| 127 if (!isDoubleBufferIndexGood) | 132 if (!isDoubleBufferIndexGood) |
| 128 return; | 133 return; |
| 129 | 134 |
| 130 AudioBuffer* inputBuffer = m_inputBuffers[doubleBufferIndex].get(); | 135 AudioBuffer* inputBuffer = m_inputBuffers[doubleBufferIndex].get(); |
| 131 AudioBuffer* outputBuffer = m_outputBuffers[doubleBufferIndex].get(); | 136 AudioBuffer* outputBuffer = m_outputBuffers[doubleBufferIndex].get(); |
| 132 | 137 |
| 133 // Check the consistency of input and output buffers. | 138 // Check the consistency of input and output buffers. |
| 134 unsigned numberOfInputChannels = m_internalInputBus->numberOfChannels(); | 139 unsigned numberOfInputChannels = m_internalInputBus->numberOfChannels(); |
| 135 bool buffersAreGood = | 140 bool buffersAreGood = |
| 136 outputBuffer && bufferSize() == outputBuffer->length() && | 141 outputBuffer && bufferSize() == outputBuffer->length() && |
| 137 m_bufferReadWriteIndex + framesToProcess <= bufferSize(); | 142 m_bufferReadWriteIndex + framesToProcess <= bufferSize(); |
| 138 | 143 |
| 139 // If the number of input channels is zero, it's ok to have inputBuffer = 0. | 144 // If the number of input channels is zero, it's ok to have inputBuffer = 0. |
| 140 if (m_internalInputBus->numberOfChannels()) | 145 if (m_internalInputBus->numberOfChannels()) |
| 141 buffersAreGood = | 146 buffersAreGood = |
| 142 buffersAreGood && inputBuffer && bufferSize() == inputBuffer->length(); | 147 buffersAreGood && inputBuffer && bufferSize() == inputBuffer->length(); |
| 143 | 148 |
| 144 DCHECK(buffersAreGood); | 149 DCHECK(buffersAreGood); |
| 145 if (!buffersAreGood) | 150 if (!buffersAreGood) |
| 146 return; | 151 return; |
| 147 | 152 |
| 148 // We assume that bufferSize() is evenly divisible by framesToProcess - should
always be true, but we should still check. | 153 // We assume that bufferSize() is evenly divisible by framesToProcess - should |
| 154 // always be true, but we should still check. |
| 149 bool isFramesToProcessGood = framesToProcess && | 155 bool isFramesToProcessGood = framesToProcess && |
| 150 bufferSize() >= framesToProcess && | 156 bufferSize() >= framesToProcess && |
| 151 !(bufferSize() % framesToProcess); | 157 !(bufferSize() % framesToProcess); |
| 152 DCHECK(isFramesToProcessGood); | 158 DCHECK(isFramesToProcessGood); |
| 153 if (!isFramesToProcessGood) | 159 if (!isFramesToProcessGood) |
| 154 return; | 160 return; |
| 155 | 161 |
| 156 unsigned numberOfOutputChannels = outputBus->numberOfChannels(); | 162 unsigned numberOfOutputChannels = outputBus->numberOfChannels(); |
| 157 | 163 |
| 158 bool channelsAreGood = (numberOfInputChannels == m_numberOfInputChannels) && | 164 bool channelsAreGood = (numberOfInputChannels == m_numberOfInputChannels) && |
| (...skipping 13 matching lines...) Expand all Loading... |
| 172 // Copy from the output buffer to the output. | 178 // Copy from the output buffer to the output. |
| 173 for (unsigned i = 0; i < numberOfOutputChannels; ++i) | 179 for (unsigned i = 0; i < numberOfOutputChannels; ++i) |
| 174 memcpy(outputBus->channel(i)->mutableData(), | 180 memcpy(outputBus->channel(i)->mutableData(), |
| 175 outputBuffer->getChannelData(i)->data() + m_bufferReadWriteIndex, | 181 outputBuffer->getChannelData(i)->data() + m_bufferReadWriteIndex, |
| 176 sizeof(float) * framesToProcess); | 182 sizeof(float) * framesToProcess); |
| 177 | 183 |
| 178 // Update the buffering index. | 184 // Update the buffering index. |
| 179 m_bufferReadWriteIndex = | 185 m_bufferReadWriteIndex = |
| 180 (m_bufferReadWriteIndex + framesToProcess) % bufferSize(); | 186 (m_bufferReadWriteIndex + framesToProcess) % bufferSize(); |
| 181 | 187 |
| 182 // m_bufferReadWriteIndex will wrap back around to 0 when the current input an
d output buffers are full. | 188 // m_bufferReadWriteIndex will wrap back around to 0 when the current input |
| 189 // and output buffers are full. |
| 183 // When this happens, fire an event and swap buffers. | 190 // When this happens, fire an event and swap buffers. |
| 184 if (!m_bufferReadWriteIndex) { | 191 if (!m_bufferReadWriteIndex) { |
| 185 // Avoid building up requests on the main thread to fire process events when
they're not being handled. | 192 // Avoid building up requests on the main thread to fire process events when |
| 186 // This could be a problem if the main thread is very busy doing other thing
s and is being held up handling previous requests. | 193 // they're not being handled. This could be a problem if the main thread is |
| 187 // The audio thread can't block on this lock, so we call tryLock() instead. | 194 // very busy doing other things and is being held up handling previous |
| 195 // requests. The audio thread can't block on this lock, so we call |
| 196 // tryLock() instead. |
| 188 MutexTryLocker tryLocker(m_processEventLock); | 197 MutexTryLocker tryLocker(m_processEventLock); |
| 189 if (!tryLocker.locked()) { | 198 if (!tryLocker.locked()) { |
| 190 // We're late in handling the previous request. The main thread must be ve
ry busy. | 199 // We're late in handling the previous request. The main thread must be |
| 191 // The best we can do is clear out the buffer ourself here. | 200 // very busy. The best we can do is clear out the buffer ourself here. |
| 192 outputBuffer->zero(); | 201 outputBuffer->zero(); |
| 193 } else if (context()->getExecutionContext()) { | 202 } else if (context()->getExecutionContext()) { |
| 194 // With the realtime context, execute the script code asynchronously | 203 // With the realtime context, execute the script code asynchronously |
| 195 // and do not wait. | 204 // and do not wait. |
| 196 if (context()->hasRealtimeConstraint()) { | 205 if (context()->hasRealtimeConstraint()) { |
| 197 // Fire the event on the main thread with the appropriate buffer | 206 // Fire the event on the main thread with the appropriate buffer |
| 198 // index. | 207 // index. |
| 199 context()->getExecutionContext()->postTask( | 208 context()->getExecutionContext()->postTask( |
| 200 BLINK_FROM_HERE, | 209 BLINK_FROM_HERE, |
| 201 createCrossThreadTask(&ScriptProcessorHandler::fireProcessEvent, | 210 createCrossThreadTask(&ScriptProcessorHandler::fireProcessEvent, |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 235 AudioBuffer* outputBuffer = m_outputBuffers[doubleBufferIndex].get(); | 244 AudioBuffer* outputBuffer = m_outputBuffers[doubleBufferIndex].get(); |
| 236 DCHECK(outputBuffer); | 245 DCHECK(outputBuffer); |
| 237 if (!outputBuffer) | 246 if (!outputBuffer) |
| 238 return; | 247 return; |
| 239 | 248 |
| 240 // Avoid firing the event if the document has already gone away. | 249 // Avoid firing the event if the document has already gone away. |
| 241 if (node() && context() && context()->getExecutionContext()) { | 250 if (node() && context() && context()->getExecutionContext()) { |
| 242 // This synchronizes with process(). | 251 // This synchronizes with process(). |
| 243 MutexLocker processLocker(m_processEventLock); | 252 MutexLocker processLocker(m_processEventLock); |
| 244 | 253 |
| 245 // Calculate a playbackTime with the buffersize which needs to be processed
each time onaudioprocess is called. | 254 // Calculate a playbackTime with the buffersize which needs to be processed |
| 246 // The outputBuffer being passed to JS will be played after exhuasting previ
ous outputBuffer by double-buffering. | 255 // each time onaudioprocess is called. The outputBuffer being passed to JS |
| 256 // will be played after exhuasting previous outputBuffer by |
| 257 // double-buffering. |
| 247 double playbackTime = (context()->currentSampleFrame() + m_bufferSize) / | 258 double playbackTime = (context()->currentSampleFrame() + m_bufferSize) / |
| 248 static_cast<double>(context()->sampleRate()); | 259 static_cast<double>(context()->sampleRate()); |
| 249 | 260 |
| 250 // Call the JavaScript event handler which will do the audio processing. | 261 // Call the JavaScript event handler which will do the audio processing. |
| 251 node()->dispatchEvent( | 262 node()->dispatchEvent( |
| 252 AudioProcessingEvent::create(inputBuffer, outputBuffer, playbackTime)); | 263 AudioProcessingEvent::create(inputBuffer, outputBuffer, playbackTime)); |
| 253 } | 264 } |
| 254 } | 265 } |
| 255 | 266 |
| 256 void ScriptProcessorHandler::fireProcessEventForOfflineAudioContext( | 267 void ScriptProcessorHandler::fireProcessEventForOfflineAudioContext( |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 325 size_t bufferSize, | 336 size_t bufferSize, |
| 326 unsigned numberOfInputChannels, | 337 unsigned numberOfInputChannels, |
| 327 unsigned numberOfOutputChannels) | 338 unsigned numberOfOutputChannels) |
| 328 : AudioNode(context), ActiveScriptWrappable(this) { | 339 : AudioNode(context), ActiveScriptWrappable(this) { |
| 329 setHandler(ScriptProcessorHandler::create(*this, sampleRate, bufferSize, | 340 setHandler(ScriptProcessorHandler::create(*this, sampleRate, bufferSize, |
| 330 numberOfInputChannels, | 341 numberOfInputChannels, |
| 331 numberOfOutputChannels)); | 342 numberOfOutputChannels)); |
| 332 } | 343 } |
| 333 | 344 |
| 334 static size_t chooseBufferSize() { | 345 static size_t chooseBufferSize() { |
| 335 // Choose a buffer size based on the audio hardware buffer size. Arbitarily ma
ke it a power of | 346 // Choose a buffer size based on the audio hardware buffer size. Arbitarily |
| 336 // two that is 4 times greater than the hardware buffer size. | 347 // make it a power of two that is 4 times greater than the hardware buffer |
| 348 // size. |
| 337 // FIXME: What is the best way to choose this? | 349 // FIXME: What is the best way to choose this? |
| 338 size_t hardwareBufferSize = Platform::current()->audioHardwareBufferSize(); | 350 size_t hardwareBufferSize = Platform::current()->audioHardwareBufferSize(); |
| 339 size_t bufferSize = | 351 size_t bufferSize = |
| 340 1 << static_cast<unsigned>(log2(4 * hardwareBufferSize) + 0.5); | 352 1 << static_cast<unsigned>(log2(4 * hardwareBufferSize) + 0.5); |
| 341 | 353 |
| 342 if (bufferSize < 256) | 354 if (bufferSize < 256) |
| 343 return 256; | 355 return 256; |
| 344 if (bufferSize > 16384) | 356 if (bufferSize > 16384) |
| 345 return 16384; | 357 return 16384; |
| 346 | 358 |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 461 | 473 |
| 462 // If |onaudioprocess| event handler is defined, the node should not be | 474 // If |onaudioprocess| event handler is defined, the node should not be |
| 463 // GCed even if it is out of scope. | 475 // GCed even if it is out of scope. |
| 464 if (hasEventListeners(EventTypeNames::audioprocess)) | 476 if (hasEventListeners(EventTypeNames::audioprocess)) |
| 465 return true; | 477 return true; |
| 466 | 478 |
| 467 return false; | 479 return false; |
| 468 } | 480 } |
| 469 | 481 |
| 470 } // namespace blink | 482 } // namespace blink |
| OLD | NEW |