| 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 * | 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 #include "public/platform/WebTraceLocation.h" | 37 #include "public/platform/WebTraceLocation.h" |
| 38 #include "wtf/PtrUtil.h" | 38 #include "wtf/PtrUtil.h" |
| 39 #include <memory> | 39 #include <memory> |
| 40 | 40 |
| 41 namespace blink { | 41 namespace blink { |
| 42 | 42 |
| 43 using namespace VectorMath; | 43 using namespace VectorMath; |
| 44 | 44 |
| 45 const int InputBufferSize = 8 * 16384; | 45 const int InputBufferSize = 8 * 16384; |
| 46 | 46 |
| 47 // We only process the leading portion of the impulse response in the real-time
thread. We don't exceed this length. | 47 // We only process the leading portion of the impulse response in the real-time |
| 48 // It turns out then, that the background thread has about 278msec of scheduling
slop. | 48 // thread. We don't exceed this length. It turns out then, that the |
| 49 // Empirically, this has been found to be a good compromise between giving enoug
h time for scheduling slop, | 49 // background thread has about 278msec of scheduling slop. Empirically, this |
| 50 // while still minimizing the amount of processing done in the primary (high-pri
ority) thread. | 50 // has been found to be a good compromise between giving enough time for |
| 51 // This was found to be a good value on Mac OS X, and may work well on other pla
tforms as well, assuming | 51 // scheduling slop, while still minimizing the amount of processing done in the |
| 52 // the very rough scheduling latencies are similar on these time-scales. Of cou
rse, this code may need to be | 52 // primary (high-priority) thread. This was found to be a good value on Mac OS |
| 53 // tuned for individual platforms if this assumption is found to be incorrect. | 53 // X, and may work well on other platforms as well, assuming the very rough |
| 54 // scheduling latencies are similar on these time-scales. Of course, this code |
| 55 // may need to be tuned for individual platforms if this assumption is found to |
| 56 // be incorrect. |
| 54 const size_t RealtimeFrameLimit = 8192 + 4096; // ~278msec @ 44.1KHz | 57 const size_t RealtimeFrameLimit = 8192 + 4096; // ~278msec @ 44.1KHz |
| 55 | 58 |
| 56 const size_t MinFFTSize = 128; | 59 const size_t MinFFTSize = 128; |
| 57 const size_t MaxRealtimeFFTSize = 2048; | 60 const size_t MaxRealtimeFFTSize = 2048; |
| 58 | 61 |
| 59 ReverbConvolver::ReverbConvolver(AudioChannel* impulseResponse, | 62 ReverbConvolver::ReverbConvolver(AudioChannel* impulseResponse, |
| 60 size_t renderSliceSize, | 63 size_t renderSliceSize, |
| 61 size_t maxFFTSize, | 64 size_t maxFFTSize, |
| 62 size_t convolverRenderPhase, | 65 size_t convolverRenderPhase, |
| 63 bool useBackgroundThreads) | 66 bool useBackgroundThreads) |
| 64 : m_impulseResponseLength(impulseResponse->length()), | 67 : m_impulseResponseLength(impulseResponse->length()), |
| 65 m_accumulationBuffer(impulseResponse->length() + renderSliceSize), | 68 m_accumulationBuffer(impulseResponse->length() + renderSliceSize), |
| 66 m_inputBuffer(InputBufferSize), | 69 m_inputBuffer(InputBufferSize), |
| 67 m_minFFTSize( | 70 m_minFFTSize(MinFFTSize), // First stage will have this size - successive |
| 68 MinFFTSize) // First stage will have this size - successive stages wi
ll double in size each time | 71 // stages will double in size each time |
| 69 , | 72 m_maxFFTSize(maxFFTSize) // until we hit m_maxFFTSize |
| 70 m_maxFFTSize(maxFFTSize) // until we hit m_maxFFTSize | |
| 71 { | 73 { |
| 72 // If we are using background threads then don't exceed this FFT size for the | 74 // If we are using background threads then don't exceed this FFT size for the |
| 73 // stages which run in the real-time thread. This avoids having only one or t
wo | 75 // stages which run in the real-time thread. This avoids having only one or |
| 74 // large stages (size 16384 or so) at the end which take a lot of time every s
everal | 76 // two large stages (size 16384 or so) at the end which take a lot of time |
| 75 // processing slices. This way we amortize the cost over more processing slic
es. | 77 // every several processing slices. This way we amortize the cost over more |
| 78 // processing slices. |
| 76 m_maxRealtimeFFTSize = MaxRealtimeFFTSize; | 79 m_maxRealtimeFFTSize = MaxRealtimeFFTSize; |
| 77 | 80 |
| 78 const float* response = impulseResponse->data(); | 81 const float* response = impulseResponse->data(); |
| 79 size_t totalResponseLength = impulseResponse->length(); | 82 size_t totalResponseLength = impulseResponse->length(); |
| 80 | 83 |
| 81 // The total latency is zero because the direct-convolution is used in the lea
ding portion. | 84 // The total latency is zero because the direct-convolution is used in the |
| 85 // leading portion. |
| 82 size_t reverbTotalLatency = 0; | 86 size_t reverbTotalLatency = 0; |
| 83 | 87 |
| 84 size_t stageOffset = 0; | 88 size_t stageOffset = 0; |
| 85 int i = 0; | 89 int i = 0; |
| 86 size_t fftSize = m_minFFTSize; | 90 size_t fftSize = m_minFFTSize; |
| 87 while (stageOffset < totalResponseLength) { | 91 while (stageOffset < totalResponseLength) { |
| 88 size_t stageSize = fftSize / 2; | 92 size_t stageSize = fftSize / 2; |
| 89 | 93 |
| 90 // For the last stage, it's possible that stageOffset is such that we're str
addling the end | 94 // For the last stage, it's possible that stageOffset is such that we're |
| 91 // of the impulse response buffer (if we use stageSize), so reduce the last
stage's length... | 95 // straddling the end of the impulse response buffer (if we use stageSize), |
| 96 // so reduce the last stage's length... |
| 92 if (stageSize + stageOffset > totalResponseLength) | 97 if (stageSize + stageOffset > totalResponseLength) |
| 93 stageSize = totalResponseLength - stageOffset; | 98 stageSize = totalResponseLength - stageOffset; |
| 94 | 99 |
| 95 // This "staggers" the time when each FFT happens so they don't all happen a
t the same time | 100 // This "staggers" the time when each FFT happens so they don't all happen |
| 101 // at the same time |
| 96 int renderPhase = convolverRenderPhase + i * renderSliceSize; | 102 int renderPhase = convolverRenderPhase + i * renderSliceSize; |
| 97 | 103 |
| 98 bool useDirectConvolver = !stageOffset; | 104 bool useDirectConvolver = !stageOffset; |
| 99 | 105 |
| 100 std::unique_ptr<ReverbConvolverStage> stage = | 106 std::unique_ptr<ReverbConvolverStage> stage = |
| 101 wrapUnique(new ReverbConvolverStage( | 107 wrapUnique(new ReverbConvolverStage( |
| 102 response, totalResponseLength, reverbTotalLatency, stageOffset, | 108 response, totalResponseLength, reverbTotalLatency, stageOffset, |
| 103 stageSize, fftSize, renderPhase, renderSliceSize, | 109 stageSize, fftSize, renderPhase, renderSliceSize, |
| 104 &m_accumulationBuffer, useDirectConvolver)); | 110 &m_accumulationBuffer, useDirectConvolver)); |
| 105 | 111 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 121 } | 127 } |
| 122 | 128 |
| 123 if (useBackgroundThreads && !isBackgroundStage && | 129 if (useBackgroundThreads && !isBackgroundStage && |
| 124 fftSize > m_maxRealtimeFFTSize) | 130 fftSize > m_maxRealtimeFFTSize) |
| 125 fftSize = m_maxRealtimeFFTSize; | 131 fftSize = m_maxRealtimeFFTSize; |
| 126 if (fftSize > m_maxFFTSize) | 132 if (fftSize > m_maxFFTSize) |
| 127 fftSize = m_maxFFTSize; | 133 fftSize = m_maxFFTSize; |
| 128 } | 134 } |
| 129 | 135 |
| 130 // Start up background thread | 136 // Start up background thread |
| 131 // FIXME: would be better to up the thread priority here. It doesn't need to
be real-time, but higher than the default... | 137 // FIXME: would be better to up the thread priority here. It doesn't need to |
| 138 // be real-time, but higher than the default... |
| 132 if (useBackgroundThreads && m_backgroundStages.size() > 0) | 139 if (useBackgroundThreads && m_backgroundStages.size() > 0) |
| 133 m_backgroundThread = wrapUnique(Platform::current()->createThread( | 140 m_backgroundThread = wrapUnique(Platform::current()->createThread( |
| 134 "Reverb convolution background thread")); | 141 "Reverb convolution background thread")); |
| 135 } | 142 } |
| 136 | 143 |
| 137 ReverbConvolver::~ReverbConvolver() { | 144 ReverbConvolver::~ReverbConvolver() { |
| 138 // Wait for background thread to stop | 145 // Wait for background thread to stop |
| 139 m_backgroundThread.reset(); | 146 m_backgroundThread.reset(); |
| 140 } | 147 } |
| 141 | 148 |
| 142 void ReverbConvolver::processInBackground() { | 149 void ReverbConvolver::processInBackground() { |
| 143 // Process all of the stages until their read indices reach the input buffer's
write index | 150 // Process all of the stages until their read indices reach the input buffer's |
| 151 // write index |
| 144 int writeIndex = m_inputBuffer.writeIndex(); | 152 int writeIndex = m_inputBuffer.writeIndex(); |
| 145 | 153 |
| 146 // Even though it doesn't seem like every stage needs to maintain its own vers
ion of readIndex | 154 // Even though it doesn't seem like every stage needs to maintain its own |
| 147 // we do this in case we want to run in more than one background thread. | 155 // version of readIndex we do this in case we want to run in more than one |
| 156 // background thread. |
| 148 int readIndex; | 157 int readIndex; |
| 149 | 158 |
| 150 while ((readIndex = m_backgroundStages[0]->inputReadIndex()) != | 159 while ((readIndex = m_backgroundStages[0]->inputReadIndex()) != |
| 151 writeIndex) { // FIXME: do better to detect buffer overrun... | 160 writeIndex) { // FIXME: do better to detect buffer overrun... |
| 152 // The ReverbConvolverStages need to process in amounts which evenly divide
half the FFT size | 161 // The ReverbConvolverStages need to process in amounts which evenly divide |
| 162 // half the FFT size |
| 153 const int SliceSize = MinFFTSize / 2; | 163 const int SliceSize = MinFFTSize / 2; |
| 154 | 164 |
| 155 // Accumulate contributions from each stage | 165 // Accumulate contributions from each stage |
| 156 for (size_t i = 0; i < m_backgroundStages.size(); ++i) | 166 for (size_t i = 0; i < m_backgroundStages.size(); ++i) |
| 157 m_backgroundStages[i]->processInBackground(this, SliceSize); | 167 m_backgroundStages[i]->processInBackground(this, SliceSize); |
| 158 } | 168 } |
| 159 } | 169 } |
| 160 | 170 |
| 161 void ReverbConvolver::process(const AudioChannel* sourceChannel, | 171 void ReverbConvolver::process(const AudioChannel* sourceChannel, |
| 162 AudioChannel* destinationChannel, | 172 AudioChannel* destinationChannel, |
| (...skipping 15 matching lines...) Expand all Loading... |
| 178 // Feed input buffer (read by all threads) | 188 // Feed input buffer (read by all threads) |
| 179 m_inputBuffer.write(source, framesToProcess); | 189 m_inputBuffer.write(source, framesToProcess); |
| 180 | 190 |
| 181 // Accumulate contributions from each stage | 191 // Accumulate contributions from each stage |
| 182 for (size_t i = 0; i < m_stages.size(); ++i) | 192 for (size_t i = 0; i < m_stages.size(); ++i) |
| 183 m_stages[i]->process(source, framesToProcess); | 193 m_stages[i]->process(source, framesToProcess); |
| 184 | 194 |
| 185 // Finally read from accumulation buffer | 195 // Finally read from accumulation buffer |
| 186 m_accumulationBuffer.readAndClear(destination, framesToProcess); | 196 m_accumulationBuffer.readAndClear(destination, framesToProcess); |
| 187 | 197 |
| 188 // Now that we've buffered more input, post another task to the background thr
ead. | 198 // Now that we've buffered more input, post another task to the background |
| 199 // thread. |
| 189 if (m_backgroundThread) | 200 if (m_backgroundThread) |
| 190 m_backgroundThread->getWebTaskRunner()->postTask( | 201 m_backgroundThread->getWebTaskRunner()->postTask( |
| 191 BLINK_FROM_HERE, crossThreadBind(&ReverbConvolver::processInBackground, | 202 BLINK_FROM_HERE, crossThreadBind(&ReverbConvolver::processInBackground, |
| 192 crossThreadUnretained(this))); | 203 crossThreadUnretained(this))); |
| 193 } | 204 } |
| 194 | 205 |
| 195 void ReverbConvolver::reset() { | 206 void ReverbConvolver::reset() { |
| 196 for (size_t i = 0; i < m_stages.size(); ++i) | 207 for (size_t i = 0; i < m_stages.size(); ++i) |
| 197 m_stages[i]->reset(); | 208 m_stages[i]->reset(); |
| 198 | 209 |
| 199 for (size_t i = 0; i < m_backgroundStages.size(); ++i) | 210 for (size_t i = 0; i < m_backgroundStages.size(); ++i) |
| 200 m_backgroundStages[i]->reset(); | 211 m_backgroundStages[i]->reset(); |
| 201 | 212 |
| 202 m_accumulationBuffer.reset(); | 213 m_accumulationBuffer.reset(); |
| 203 m_inputBuffer.reset(); | 214 m_inputBuffer.reset(); |
| 204 } | 215 } |
| 205 | 216 |
| 206 size_t ReverbConvolver::latencyFrames() const { | 217 size_t ReverbConvolver::latencyFrames() const { |
| 207 return 0; | 218 return 0; |
| 208 } | 219 } |
| 209 | 220 |
| 210 } // namespace blink | 221 } // namespace blink |
| OLD | NEW |