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 22 matching lines...) Expand all Loading... |
33 #include <complex> | 33 #include <complex> |
34 #include <limits.h> | 34 #include <limits.h> |
35 | 35 |
36 namespace blink { | 36 namespace blink { |
37 | 37 |
38 const double RealtimeAnalyser::DefaultSmoothingTimeConstant = 0.8; | 38 const double RealtimeAnalyser::DefaultSmoothingTimeConstant = 0.8; |
39 const double RealtimeAnalyser::DefaultMinDecibels = -100; | 39 const double RealtimeAnalyser::DefaultMinDecibels = -100; |
40 const double RealtimeAnalyser::DefaultMaxDecibels = -30; | 40 const double RealtimeAnalyser::DefaultMaxDecibels = -30; |
41 | 41 |
42 const unsigned RealtimeAnalyser::DefaultFFTSize = 2048; | 42 const unsigned RealtimeAnalyser::DefaultFFTSize = 2048; |
43 // All FFT implementations are expected to handle power-of-two sizes MinFFTSize
<= size <= MaxFFTSize. | 43 // All FFT implementations are expected to handle power-of-two sizes |
| 44 // MinFFTSize <= size <= MaxFFTSize. |
44 const unsigned RealtimeAnalyser::MinFFTSize = 32; | 45 const unsigned RealtimeAnalyser::MinFFTSize = 32; |
45 const unsigned RealtimeAnalyser::MaxFFTSize = 32768; | 46 const unsigned RealtimeAnalyser::MaxFFTSize = 32768; |
46 const unsigned RealtimeAnalyser::InputBufferSize = | 47 const unsigned RealtimeAnalyser::InputBufferSize = |
47 RealtimeAnalyser::MaxFFTSize * 2; | 48 RealtimeAnalyser::MaxFFTSize * 2; |
48 | 49 |
49 RealtimeAnalyser::RealtimeAnalyser() | 50 RealtimeAnalyser::RealtimeAnalyser() |
50 : m_inputBuffer(InputBufferSize), | 51 : m_inputBuffer(InputBufferSize), |
51 m_writeIndex(0), | 52 m_writeIndex(0), |
52 m_downMixBus(AudioBus::create(1, AudioUtilities::kRenderQuantumFrames)), | 53 m_downMixBus(AudioBus::create(1, AudioUtilities::kRenderQuantumFrames)), |
53 m_fftSize(DefaultFFTSize), | 54 m_fftSize(DefaultFFTSize), |
(...skipping 10 matching lines...) Expand all Loading... |
64 | 65 |
65 // Only allow powers of two. | 66 // Only allow powers of two. |
66 unsigned log2size = static_cast<unsigned>(log2(size)); | 67 unsigned log2size = static_cast<unsigned>(log2(size)); |
67 bool isPOT(1UL << log2size == size); | 68 bool isPOT(1UL << log2size == size); |
68 | 69 |
69 if (!isPOT || size > MaxFFTSize || size < MinFFTSize) | 70 if (!isPOT || size > MaxFFTSize || size < MinFFTSize) |
70 return false; | 71 return false; |
71 | 72 |
72 if (m_fftSize != size) { | 73 if (m_fftSize != size) { |
73 m_analysisFrame = wrapUnique(new FFTFrame(size)); | 74 m_analysisFrame = wrapUnique(new FFTFrame(size)); |
74 // m_magnitudeBuffer has size = fftSize / 2 because it contains floats reduc
ed from complex values in m_analysisFrame. | 75 // m_magnitudeBuffer has size = fftSize / 2 because it contains floats |
| 76 // reduced from complex values in m_analysisFrame. |
75 m_magnitudeBuffer.allocate(size / 2); | 77 m_magnitudeBuffer.allocate(size / 2); |
76 m_fftSize = size; | 78 m_fftSize = size; |
77 } | 79 } |
78 | 80 |
79 return true; | 81 return true; |
80 } | 82 } |
81 | 83 |
82 void RealtimeAnalyser::writeInput(AudioBus* bus, size_t framesToProcess) { | 84 void RealtimeAnalyser::writeInput(AudioBus* bus, size_t framesToProcess) { |
83 bool isBusGood = bus && bus->numberOfChannels() > 0 && | 85 bool isBusGood = bus && bus->numberOfChannels() > 0 && |
84 bus->channel(0)->length() >= framesToProcess; | 86 bus->channel(0)->length() >= framesToProcess; |
85 DCHECK(isBusGood); | 87 DCHECK(isBusGood); |
86 if (!isBusGood) | 88 if (!isBusGood) |
87 return; | 89 return; |
88 | 90 |
89 // FIXME : allow to work with non-FFTSize divisible chunking | 91 // FIXME : allow to work with non-FFTSize divisible chunking |
90 bool isDestinationGood = | 92 bool isDestinationGood = |
91 m_writeIndex < m_inputBuffer.size() && | 93 m_writeIndex < m_inputBuffer.size() && |
92 m_writeIndex + framesToProcess <= m_inputBuffer.size(); | 94 m_writeIndex + framesToProcess <= m_inputBuffer.size(); |
93 DCHECK(isDestinationGood); | 95 DCHECK(isDestinationGood); |
94 if (!isDestinationGood) | 96 if (!isDestinationGood) |
95 return; | 97 return; |
96 | 98 |
97 // Perform real-time analysis | 99 // Perform real-time analysis |
98 float* dest = m_inputBuffer.data() + m_writeIndex; | 100 float* dest = m_inputBuffer.data() + m_writeIndex; |
99 | 101 |
100 // Clear the bus and downmix the input according to the down mixing rules. Th
en save the result | 102 // Clear the bus and downmix the input according to the down mixing rules. |
101 // in the m_inputBuffer at the appropriate place. | 103 // Then save the result in the m_inputBuffer at the appropriate place. |
102 m_downMixBus->zero(); | 104 m_downMixBus->zero(); |
103 m_downMixBus->sumFrom(*bus); | 105 m_downMixBus->sumFrom(*bus); |
104 memcpy(dest, m_downMixBus->channel(0)->data(), | 106 memcpy(dest, m_downMixBus->channel(0)->data(), |
105 framesToProcess * sizeof(*dest)); | 107 framesToProcess * sizeof(*dest)); |
106 | 108 |
107 m_writeIndex += framesToProcess; | 109 m_writeIndex += framesToProcess; |
108 if (m_writeIndex >= InputBufferSize) | 110 if (m_writeIndex >= InputBufferSize) |
109 m_writeIndex = 0; | 111 m_writeIndex = 0; |
110 } | 112 } |
111 | 113 |
(...skipping 14 matching lines...) Expand all Loading... |
126 a0 - a1 * cos(twoPiDouble * x) + a2 * cos(twoPiDouble * 2.0 * x); | 128 a0 - a1 * cos(twoPiDouble * x) + a2 * cos(twoPiDouble * 2.0 * x); |
127 p[i] *= float(window); | 129 p[i] *= float(window); |
128 } | 130 } |
129 } | 131 } |
130 | 132 |
131 } // namespace | 133 } // namespace |
132 | 134 |
133 void RealtimeAnalyser::doFFTAnalysis() { | 135 void RealtimeAnalyser::doFFTAnalysis() { |
134 DCHECK(isMainThread()); | 136 DCHECK(isMainThread()); |
135 | 137 |
136 // Unroll the input buffer into a temporary buffer, where we'll apply an analy
sis window followed by an FFT. | 138 // Unroll the input buffer into a temporary buffer, where we'll apply an |
| 139 // analysis window followed by an FFT. |
137 size_t fftSize = this->fftSize(); | 140 size_t fftSize = this->fftSize(); |
138 | 141 |
139 AudioFloatArray temporaryBuffer(fftSize); | 142 AudioFloatArray temporaryBuffer(fftSize); |
140 float* inputBuffer = m_inputBuffer.data(); | 143 float* inputBuffer = m_inputBuffer.data(); |
141 float* tempP = temporaryBuffer.data(); | 144 float* tempP = temporaryBuffer.data(); |
142 | 145 |
143 // Take the previous fftSize values from the input buffer and copy into the te
mporary buffer. | 146 // Take the previous fftSize values from the input buffer and copy into the |
| 147 // temporary buffer. |
144 unsigned writeIndex = m_writeIndex; | 148 unsigned writeIndex = m_writeIndex; |
145 if (writeIndex < fftSize) { | 149 if (writeIndex < fftSize) { |
146 memcpy(tempP, inputBuffer + writeIndex - fftSize + InputBufferSize, | 150 memcpy(tempP, inputBuffer + writeIndex - fftSize + InputBufferSize, |
147 sizeof(*tempP) * (fftSize - writeIndex)); | 151 sizeof(*tempP) * (fftSize - writeIndex)); |
148 memcpy(tempP + fftSize - writeIndex, inputBuffer, | 152 memcpy(tempP + fftSize - writeIndex, inputBuffer, |
149 sizeof(*tempP) * writeIndex); | 153 sizeof(*tempP) * writeIndex); |
150 } else { | 154 } else { |
151 memcpy(tempP, inputBuffer + writeIndex - fftSize, sizeof(*tempP) * fftSize); | 155 memcpy(tempP, inputBuffer + writeIndex - fftSize, sizeof(*tempP) * fftSize); |
152 } | 156 } |
153 | 157 |
154 // Window the input samples. | 158 // Window the input samples. |
155 applyWindow(tempP, fftSize); | 159 applyWindow(tempP, fftSize); |
156 | 160 |
157 // Do the analysis. | 161 // Do the analysis. |
158 m_analysisFrame->doFFT(tempP); | 162 m_analysisFrame->doFFT(tempP); |
159 | 163 |
160 float* realP = m_analysisFrame->realData(); | 164 float* realP = m_analysisFrame->realData(); |
161 float* imagP = m_analysisFrame->imagData(); | 165 float* imagP = m_analysisFrame->imagData(); |
162 | 166 |
163 // Blow away the packed nyquist component. | 167 // Blow away the packed nyquist component. |
164 imagP[0] = 0; | 168 imagP[0] = 0; |
165 | 169 |
166 // Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FFT
scaling factor). | 170 // Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FFT |
| 171 // scaling factor). |
167 const double magnitudeScale = 1.0 / fftSize; | 172 const double magnitudeScale = 1.0 / fftSize; |
168 | 173 |
169 // A value of 0 does no averaging with the previous result. Larger values pro
duce slower, but smoother changes. | 174 // A value of 0 does no averaging with the previous result. Larger values |
| 175 // produce slower, but smoother changes. |
170 double k = m_smoothingTimeConstant; | 176 double k = m_smoothingTimeConstant; |
171 k = std::max(0.0, k); | 177 k = std::max(0.0, k); |
172 k = std::min(1.0, k); | 178 k = std::min(1.0, k); |
173 | 179 |
174 // Convert the analysis data from complex to magnitude and average with the pr
evious result. | 180 // Convert the analysis data from complex to magnitude and average with the |
| 181 // previous result. |
175 float* destination = magnitudeBuffer().data(); | 182 float* destination = magnitudeBuffer().data(); |
176 size_t n = magnitudeBuffer().size(); | 183 size_t n = magnitudeBuffer().size(); |
177 for (size_t i = 0; i < n; ++i) { | 184 for (size_t i = 0; i < n; ++i) { |
178 std::complex<double> c(realP[i], imagP[i]); | 185 std::complex<double> c(realP[i], imagP[i]); |
179 double scalarMagnitude = abs(c) * magnitudeScale; | 186 double scalarMagnitude = abs(c) * magnitudeScale; |
180 destination[i] = float(k * destination[i] + (1 - k) * scalarMagnitude); | 187 destination[i] = float(k * destination[i] + (1 - k) * scalarMagnitude); |
181 } | 188 } |
182 } | 189 } |
183 | 190 |
184 void RealtimeAnalyser::convertFloatToDb(DOMFloat32Array* destinationArray) { | 191 void RealtimeAnalyser::convertFloatToDb(DOMFloat32Array* destinationArray) { |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
224 : 1 / (m_maxDecibels - m_minDecibels); | 231 : 1 / (m_maxDecibels - m_minDecibels); |
225 const double minDecibels = m_minDecibels; | 232 const double minDecibels = m_minDecibels; |
226 | 233 |
227 const float* source = magnitudeBuffer().data(); | 234 const float* source = magnitudeBuffer().data(); |
228 unsigned char* destination = destinationArray->data(); | 235 unsigned char* destination = destinationArray->data(); |
229 | 236 |
230 for (unsigned i = 0; i < len; ++i) { | 237 for (unsigned i = 0; i < len; ++i) { |
231 float linearValue = source[i]; | 238 float linearValue = source[i]; |
232 double dbMag = AudioUtilities::linearToDecibels(linearValue); | 239 double dbMag = AudioUtilities::linearToDecibels(linearValue); |
233 | 240 |
234 // The range m_minDecibels to m_maxDecibels will be scaled to byte values
from 0 to UCHAR_MAX. | 241 // The range m_minDecibels to m_maxDecibels will be scaled to byte values |
| 242 // from 0 to UCHAR_MAX. |
235 double scaledValue = UCHAR_MAX * (dbMag - minDecibels) * rangeScaleFactor; | 243 double scaledValue = UCHAR_MAX * (dbMag - minDecibels) * rangeScaleFactor; |
236 | 244 |
237 // Clip to valid range. | 245 // Clip to valid range. |
238 if (scaledValue < 0) | 246 if (scaledValue < 0) |
239 scaledValue = 0; | 247 scaledValue = 0; |
240 if (scaledValue > UCHAR_MAX) | 248 if (scaledValue > UCHAR_MAX) |
241 scaledValue = UCHAR_MAX; | 249 scaledValue = UCHAR_MAX; |
242 | 250 |
243 destination[i] = static_cast<unsigned char>(scaledValue); | 251 destination[i] = static_cast<unsigned char>(scaledValue); |
244 } | 252 } |
245 } | 253 } |
246 } | 254 } |
247 | 255 |
248 void RealtimeAnalyser::getByteFrequencyData(DOMUint8Array* destinationArray, | 256 void RealtimeAnalyser::getByteFrequencyData(DOMUint8Array* destinationArray, |
249 double currentTime) { | 257 double currentTime) { |
250 DCHECK(isMainThread()); | 258 DCHECK(isMainThread()); |
251 DCHECK(destinationArray); | 259 DCHECK(destinationArray); |
252 | 260 |
253 if (currentTime <= m_lastAnalysisTime) { | 261 if (currentTime <= m_lastAnalysisTime) { |
254 // FIXME: Is it worth caching the data so we don't have to do the conversion
every time? | 262 // FIXME: Is it worth caching the data so we don't have to do the conversion |
255 // Perhaps not, since we expect many calls in the same rendering quantum. | 263 // every time? Perhaps not, since we expect many calls in the same |
| 264 // rendering quantum. |
256 convertToByteData(destinationArray); | 265 convertToByteData(destinationArray); |
257 return; | 266 return; |
258 } | 267 } |
259 | 268 |
260 // Time has advanced since the last call; update the FFT data. | 269 // Time has advanced since the last call; update the FFT data. |
261 m_lastAnalysisTime = currentTime; | 270 m_lastAnalysisTime = currentTime; |
262 doFFTAnalysis(); | 271 doFFTAnalysis(); |
263 | 272 |
264 convertToByteData(destinationArray); | 273 convertToByteData(destinationArray); |
265 } | 274 } |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
324 scaledValue = 0; | 333 scaledValue = 0; |
325 if (scaledValue > UCHAR_MAX) | 334 if (scaledValue > UCHAR_MAX) |
326 scaledValue = UCHAR_MAX; | 335 scaledValue = UCHAR_MAX; |
327 | 336 |
328 destination[i] = static_cast<unsigned char>(scaledValue); | 337 destination[i] = static_cast<unsigned char>(scaledValue); |
329 } | 338 } |
330 } | 339 } |
331 } | 340 } |
332 | 341 |
333 } // namespace blink | 342 } // namespace blink |
OLD | NEW |