| 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 | 
|---|