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 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
53 , m_smoothingTimeConstant(DefaultSmoothingTimeConstant) | 53 , m_smoothingTimeConstant(DefaultSmoothingTimeConstant) |
54 , m_minDecibels(DefaultMinDecibels) | 54 , m_minDecibels(DefaultMinDecibels) |
55 , m_maxDecibels(DefaultMaxDecibels) | 55 , m_maxDecibels(DefaultMaxDecibels) |
56 , m_lastAnalysisTime(-1) | 56 , m_lastAnalysisTime(-1) |
57 { | 57 { |
58 m_analysisFrame = wrapUnique(new FFTFrame(DefaultFFTSize)); | 58 m_analysisFrame = wrapUnique(new FFTFrame(DefaultFFTSize)); |
59 } | 59 } |
60 | 60 |
61 bool RealtimeAnalyser::setFftSize(size_t size) | 61 bool RealtimeAnalyser::setFftSize(size_t size) |
62 { | 62 { |
63 ASSERT(isMainThread()); | 63 DCHECK(isMainThread()); |
64 | 64 |
65 // Only allow powers of two. | 65 // Only allow powers of two. |
66 unsigned log2size = static_cast<unsigned>(log2(size)); | 66 unsigned log2size = static_cast<unsigned>(log2(size)); |
67 bool isPOT(1UL << log2size == size); | 67 bool isPOT(1UL << log2size == size); |
68 | 68 |
69 if (!isPOT || size > MaxFFTSize || size < MinFFTSize) | 69 if (!isPOT || size > MaxFFTSize || size < MinFFTSize) |
70 return false; | 70 return false; |
71 | 71 |
72 if (m_fftSize != size) { | 72 if (m_fftSize != size) { |
73 m_analysisFrame = wrapUnique(new FFTFrame(size)); | 73 m_analysisFrame = wrapUnique(new FFTFrame(size)); |
74 // m_magnitudeBuffer has size = fftSize / 2 because it contains floats r
educed from complex values in m_analysisFrame. | 74 // m_magnitudeBuffer has size = fftSize / 2 because it contains floats r
educed from complex values in m_analysisFrame. |
75 m_magnitudeBuffer.allocate(size / 2); | 75 m_magnitudeBuffer.allocate(size / 2); |
76 m_fftSize = size; | 76 m_fftSize = size; |
77 } | 77 } |
78 | 78 |
79 return true; | 79 return true; |
80 } | 80 } |
81 | 81 |
82 void RealtimeAnalyser::writeInput(AudioBus* bus, size_t framesToProcess) | 82 void RealtimeAnalyser::writeInput(AudioBus* bus, size_t framesToProcess) |
83 { | 83 { |
84 bool isBusGood = bus && bus->numberOfChannels() > 0 && bus->channel(0)->leng
th() >= framesToProcess; | 84 bool isBusGood = bus && bus->numberOfChannels() > 0 && bus->channel(0)->leng
th() >= framesToProcess; |
85 ASSERT(isBusGood); | 85 DCHECK(isBusGood); |
86 if (!isBusGood) | 86 if (!isBusGood) |
87 return; | 87 return; |
88 | 88 |
89 // FIXME : allow to work with non-FFTSize divisible chunking | 89 // FIXME : allow to work with non-FFTSize divisible chunking |
90 bool isDestinationGood = m_writeIndex < m_inputBuffer.size() && m_writeIndex
+ framesToProcess <= m_inputBuffer.size(); | 90 bool isDestinationGood = m_writeIndex < m_inputBuffer.size() && m_writeIndex
+ framesToProcess <= m_inputBuffer.size(); |
91 ASSERT(isDestinationGood); | 91 DCHECK(isDestinationGood); |
92 if (!isDestinationGood) | 92 if (!isDestinationGood) |
93 return; | 93 return; |
94 | 94 |
95 // Perform real-time analysis | 95 // Perform real-time analysis |
96 float* dest = m_inputBuffer.data() + m_writeIndex; | 96 float* dest = m_inputBuffer.data() + m_writeIndex; |
97 | 97 |
98 // Clear the bus and downmix the input according to the down mixing rules.
Then save the result | 98 // Clear the bus and downmix the input according to the down mixing rules.
Then save the result |
99 // in the m_inputBuffer at the appropriate place. | 99 // in the m_inputBuffer at the appropriate place. |
100 m_downMixBus->zero(); | 100 m_downMixBus->zero(); |
101 m_downMixBus->sumFrom(*bus); | 101 m_downMixBus->sumFrom(*bus); |
102 memcpy(dest, m_downMixBus->channel(0)->data(), framesToProcess * sizeof(*des
t)); | 102 memcpy(dest, m_downMixBus->channel(0)->data(), framesToProcess * sizeof(*des
t)); |
103 | 103 |
104 m_writeIndex += framesToProcess; | 104 m_writeIndex += framesToProcess; |
105 if (m_writeIndex >= InputBufferSize) | 105 if (m_writeIndex >= InputBufferSize) |
106 m_writeIndex = 0; | 106 m_writeIndex = 0; |
107 } | 107 } |
108 | 108 |
109 namespace { | 109 namespace { |
110 | 110 |
111 void applyWindow(float* p, size_t n) | 111 void applyWindow(float* p, size_t n) |
112 { | 112 { |
113 ASSERT(isMainThread()); | 113 DCHECK(isMainThread()); |
114 | 114 |
115 // Blackman window | 115 // Blackman window |
116 double alpha = 0.16; | 116 double alpha = 0.16; |
117 double a0 = 0.5 * (1 - alpha); | 117 double a0 = 0.5 * (1 - alpha); |
118 double a1 = 0.5; | 118 double a1 = 0.5; |
119 double a2 = 0.5 * alpha; | 119 double a2 = 0.5 * alpha; |
120 | 120 |
121 for (unsigned i = 0; i < n; ++i) { | 121 for (unsigned i = 0; i < n; ++i) { |
122 double x = static_cast<double>(i) / static_cast<double>(n); | 122 double x = static_cast<double>(i) / static_cast<double>(n); |
123 double window = a0 - a1 * cos(twoPiDouble * x) + a2 * cos(twoPiDouble *
2.0 * x); | 123 double window = a0 - a1 * cos(twoPiDouble * x) + a2 * cos(twoPiDouble *
2.0 * x); |
124 p[i] *= float(window); | 124 p[i] *= float(window); |
125 } | 125 } |
126 } | 126 } |
127 | 127 |
128 } // namespace | 128 } // namespace |
129 | 129 |
130 void RealtimeAnalyser::doFFTAnalysis() | 130 void RealtimeAnalyser::doFFTAnalysis() |
131 { | 131 { |
132 ASSERT(isMainThread()); | 132 DCHECK(isMainThread()); |
133 | 133 |
134 // Unroll the input buffer into a temporary buffer, where we'll apply an ana
lysis window followed by an FFT. | 134 // Unroll the input buffer into a temporary buffer, where we'll apply an ana
lysis window followed by an FFT. |
135 size_t fftSize = this->fftSize(); | 135 size_t fftSize = this->fftSize(); |
136 | 136 |
137 AudioFloatArray temporaryBuffer(fftSize); | 137 AudioFloatArray temporaryBuffer(fftSize); |
138 float* inputBuffer = m_inputBuffer.data(); | 138 float* inputBuffer = m_inputBuffer.data(); |
139 float* tempP = temporaryBuffer.data(); | 139 float* tempP = temporaryBuffer.data(); |
140 | 140 |
141 // Take the previous fftSize values from the input buffer and copy into the
temporary buffer. | 141 // Take the previous fftSize values from the input buffer and copy into the
temporary buffer. |
142 unsigned writeIndex = m_writeIndex; | 142 unsigned writeIndex = m_writeIndex; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
189 for (unsigned i = 0; i < len; ++i) { | 189 for (unsigned i = 0; i < len; ++i) { |
190 float linearValue = source[i]; | 190 float linearValue = source[i]; |
191 double dbMag = AudioUtilities::linearToDecibels(linearValue); | 191 double dbMag = AudioUtilities::linearToDecibels(linearValue); |
192 destination[i] = float(dbMag); | 192 destination[i] = float(dbMag); |
193 } | 193 } |
194 } | 194 } |
195 } | 195 } |
196 | 196 |
197 void RealtimeAnalyser::getFloatFrequencyData(DOMFloat32Array* destinationArray,
double currentTime) | 197 void RealtimeAnalyser::getFloatFrequencyData(DOMFloat32Array* destinationArray,
double currentTime) |
198 { | 198 { |
199 ASSERT(isMainThread()); | 199 DCHECK(isMainThread()); |
200 ASSERT(destinationArray); | 200 DCHECK(destinationArray); |
201 | 201 |
202 if (currentTime <= m_lastAnalysisTime) { | 202 if (currentTime <= m_lastAnalysisTime) { |
203 convertFloatToDb(destinationArray); | 203 convertFloatToDb(destinationArray); |
204 return; | 204 return; |
205 } | 205 } |
206 | 206 |
207 // Time has advanced since the last call; update the FFT data. | 207 // Time has advanced since the last call; update the FFT data. |
208 m_lastAnalysisTime = currentTime; | 208 m_lastAnalysisTime = currentTime; |
209 doFFTAnalysis(); | 209 doFFTAnalysis(); |
210 | 210 |
(...skipping 25 matching lines...) Expand all Loading... |
236 if (scaledValue > UCHAR_MAX) | 236 if (scaledValue > UCHAR_MAX) |
237 scaledValue = UCHAR_MAX; | 237 scaledValue = UCHAR_MAX; |
238 | 238 |
239 destination[i] = static_cast<unsigned char>(scaledValue); | 239 destination[i] = static_cast<unsigned char>(scaledValue); |
240 } | 240 } |
241 } | 241 } |
242 } | 242 } |
243 | 243 |
244 void RealtimeAnalyser::getByteFrequencyData(DOMUint8Array* destinationArray, dou
ble currentTime) | 244 void RealtimeAnalyser::getByteFrequencyData(DOMUint8Array* destinationArray, dou
ble currentTime) |
245 { | 245 { |
246 ASSERT(isMainThread()); | 246 DCHECK(isMainThread()); |
247 ASSERT(destinationArray); | 247 DCHECK(destinationArray); |
248 | 248 |
249 if (currentTime <= m_lastAnalysisTime) { | 249 if (currentTime <= m_lastAnalysisTime) { |
250 // FIXME: Is it worth caching the data so we don't have to do the conver
sion every time? | 250 // FIXME: Is it worth caching the data so we don't have to do the conver
sion every time? |
251 // Perhaps not, since we expect many calls in the same rendering quantum
. | 251 // Perhaps not, since we expect many calls in the same rendering quantum
. |
252 convertToByteData(destinationArray); | 252 convertToByteData(destinationArray); |
253 return; | 253 return; |
254 } | 254 } |
255 | 255 |
256 // Time has advanced since the last call; update the FFT data. | 256 // Time has advanced since the last call; update the FFT data. |
257 m_lastAnalysisTime = currentTime; | 257 m_lastAnalysisTime = currentTime; |
258 doFFTAnalysis(); | 258 doFFTAnalysis(); |
259 | 259 |
260 convertToByteData(destinationArray); | 260 convertToByteData(destinationArray); |
261 } | 261 } |
262 | 262 |
263 void RealtimeAnalyser::getFloatTimeDomainData(DOMFloat32Array* destinationArray) | 263 void RealtimeAnalyser::getFloatTimeDomainData(DOMFloat32Array* destinationArray) |
264 { | 264 { |
265 ASSERT(isMainThread()); | 265 DCHECK(isMainThread()); |
266 ASSERT(destinationArray); | 266 DCHECK(destinationArray); |
267 | 267 |
268 unsigned fftSize = this->fftSize(); | 268 unsigned fftSize = this->fftSize(); |
269 size_t len = std::min(fftSize, destinationArray->length()); | 269 size_t len = std::min(fftSize, destinationArray->length()); |
270 if (len > 0) { | 270 if (len > 0) { |
271 bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_in
putBuffer.size() > fftSize; | 271 bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_in
putBuffer.size() > fftSize; |
272 ASSERT(isInputBufferGood); | 272 DCHECK(isInputBufferGood); |
273 if (!isInputBufferGood) | 273 if (!isInputBufferGood) |
274 return; | 274 return; |
275 | 275 |
276 float* inputBuffer = m_inputBuffer.data(); | 276 float* inputBuffer = m_inputBuffer.data(); |
277 float* destination = destinationArray->data(); | 277 float* destination = destinationArray->data(); |
278 | 278 |
279 unsigned writeIndex = m_writeIndex; | 279 unsigned writeIndex = m_writeIndex; |
280 | 280 |
281 for (unsigned i = 0; i < len; ++i) { | 281 for (unsigned i = 0; i < len; ++i) { |
282 // Buffer access is protected due to modulo operation. | 282 // Buffer access is protected due to modulo operation. |
283 float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSiz
e) % InputBufferSize]; | 283 float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSiz
e) % InputBufferSize]; |
284 | 284 |
285 destination[i] = value; | 285 destination[i] = value; |
286 } | 286 } |
287 } | 287 } |
288 } | 288 } |
289 | 289 |
290 void RealtimeAnalyser::getByteTimeDomainData(DOMUint8Array* destinationArray) | 290 void RealtimeAnalyser::getByteTimeDomainData(DOMUint8Array* destinationArray) |
291 { | 291 { |
292 ASSERT(isMainThread()); | 292 DCHECK(isMainThread()); |
293 ASSERT(destinationArray); | 293 DCHECK(destinationArray); |
294 | 294 |
295 unsigned fftSize = this->fftSize(); | 295 unsigned fftSize = this->fftSize(); |
296 size_t len = std::min(fftSize, destinationArray->length()); | 296 size_t len = std::min(fftSize, destinationArray->length()); |
297 if (len > 0) { | 297 if (len > 0) { |
298 bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_in
putBuffer.size() > fftSize; | 298 bool isInputBufferGood = m_inputBuffer.size() == InputBufferSize && m_in
putBuffer.size() > fftSize; |
299 ASSERT(isInputBufferGood); | 299 DCHECK(isInputBufferGood); |
300 if (!isInputBufferGood) | 300 if (!isInputBufferGood) |
301 return; | 301 return; |
302 | 302 |
303 float* inputBuffer = m_inputBuffer.data(); | 303 float* inputBuffer = m_inputBuffer.data(); |
304 unsigned char* destination = destinationArray->data(); | 304 unsigned char* destination = destinationArray->data(); |
305 | 305 |
306 unsigned writeIndex = m_writeIndex; | 306 unsigned writeIndex = m_writeIndex; |
307 | 307 |
308 for (unsigned i = 0; i < len; ++i) { | 308 for (unsigned i = 0; i < len; ++i) { |
309 // Buffer access is protected due to modulo operation. | 309 // Buffer access is protected due to modulo operation. |
310 float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSiz
e) % InputBufferSize]; | 310 float value = inputBuffer[(i + writeIndex - fftSize + InputBufferSiz
e) % InputBufferSize]; |
311 | 311 |
312 // Scale from nominal -1 -> +1 to unsigned byte. | 312 // Scale from nominal -1 -> +1 to unsigned byte. |
313 double scaledValue = 128 * (value + 1); | 313 double scaledValue = 128 * (value + 1); |
314 | 314 |
315 // Clip to valid range. | 315 // Clip to valid range. |
316 if (scaledValue < 0) | 316 if (scaledValue < 0) |
317 scaledValue = 0; | 317 scaledValue = 0; |
318 if (scaledValue > UCHAR_MAX) | 318 if (scaledValue > UCHAR_MAX) |
319 scaledValue = UCHAR_MAX; | 319 scaledValue = UCHAR_MAX; |
320 | 320 |
321 destination[i] = static_cast<unsigned char>(scaledValue); | 321 destination[i] = static_cast<unsigned char>(scaledValue); |
322 } | 322 } |
323 } | 323 } |
324 } | 324 } |
325 | 325 |
326 } // namespace blink | 326 } // namespace blink |
327 | 327 |
OLD | NEW |