1 /* 2 ** 3 ** Copyright 2010, The Android Open Source Project 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 18 19 //#define LOG_NDEBUG 0 20 #define LOG_TAG "Visualizer" 21 #include <utils/Log.h> 22 23 #include <stdint.h> 24 #include <sys/types.h> 25 #include <limits.h> 26 27 #include <media/Visualizer.h> 28 29 extern void fixed_fft_real(int n, int32_t *v); 30 31 namespace android { 32 33 // --------------------------------------------------------------------------- 34 35 Visualizer::Visualizer (int32_t priority, 36 effect_callback_t cbf, 37 void* user, 38 int sessionId) 39 : AudioEffect(SL_IID_VISUALIZATION, NULL, priority, cbf, user, sessionId), 40 mCaptureRate(CAPTURE_RATE_DEF), 41 mCaptureSize(CAPTURE_SIZE_DEF), 42 mSampleRate(44100000), 43 mCaptureCallBack(NULL), 44 mCaptureCbkUser(NULL) 45 { 46 initCaptureSize(); 47 } 48 49 Visualizer::~Visualizer() 50 { 51 } 52 53 status_t Visualizer::setEnabled(bool enabled) 54 { 55 Mutex::Autolock _l(mLock); 56 57 sp<CaptureThread> t = mCaptureThread; 58 if (t != 0) { 59 if (enabled) { 60 if (t->exitPending()) { 61 if (t->requestExitAndWait() == WOULD_BLOCK) { 62 LOGE("Visualizer::enable() called from thread"); 63 return INVALID_OPERATION; 64 } 65 } 66 } 67 t->mLock.lock(); 68 } 69 70 status_t status = AudioEffect::setEnabled(enabled); 71 72 if (status == NO_ERROR) { 73 if (t != 0) { 74 if (enabled) { 75 t->run("AudioTrackThread"); 76 } else { 77 t->requestExit(); 78 } 79 } 80 } 81 82 if (t != 0) { 83 t->mLock.unlock(); 84 } 85 86 return status; 87 } 88 89 status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, uint32_t rate) 90 { 91 if (rate > CAPTURE_RATE_MAX) { 92 return BAD_VALUE; 93 } 94 Mutex::Autolock _l(mLock); 95 96 if (mEnabled) { 97 return INVALID_OPERATION; 98 } 99 100 sp<CaptureThread> t = mCaptureThread; 101 if (t != 0) { 102 t->mLock.lock(); 103 } 104 mCaptureThread.clear(); 105 mCaptureCallBack = cbk; 106 mCaptureCbkUser = user; 107 mCaptureFlags = flags; 108 mCaptureRate = rate; 109 110 if (t != 0) { 111 t->mLock.unlock(); 112 } 113 114 if (cbk != NULL) { 115 mCaptureThread = new CaptureThread(*this, rate, ((flags & CAPTURE_CALL_JAVA) != 0)); 116 if (mCaptureThread == 0) { 117 LOGE("Could not create callback thread"); 118 return NO_INIT; 119 } 120 } 121 LOGV("setCaptureCallBack() rate: %d thread %p flags 0x%08x", 122 rate, mCaptureThread.get(), mCaptureFlags); 123 return NO_ERROR; 124 } 125 126 status_t Visualizer::setCaptureSize(uint32_t size) 127 { 128 if (size > VISUALIZER_CAPTURE_SIZE_MAX || 129 size < VISUALIZER_CAPTURE_SIZE_MIN || 130 AudioSystem::popCount(size) != 1) { 131 return BAD_VALUE; 132 } 133 134 Mutex::Autolock _l(mLock); 135 if (mEnabled) { 136 return INVALID_OPERATION; 137 } 138 139 uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2]; 140 effect_param_t *p = (effect_param_t *)buf32; 141 142 p->psize = sizeof(uint32_t); 143 p->vsize = sizeof(uint32_t); 144 *(int32_t *)p->data = VISU_PARAM_CAPTURE_SIZE; 145 *((int32_t *)p->data + 1)= size; 146 status_t status = setParameter(p); 147 148 LOGV("setCaptureSize size %d status %d p->status %d", size, status, p->status); 149 150 if (status == NO_ERROR) { 151 status = p->status; 152 } 153 if (status == NO_ERROR) { 154 mCaptureSize = size; 155 } 156 157 return status; 158 } 159 160 status_t Visualizer::getWaveForm(uint8_t *waveform) 161 { 162 if (waveform == NULL) { 163 return BAD_VALUE; 164 } 165 if (mCaptureSize == 0) { 166 return NO_INIT; 167 } 168 169 status_t status = NO_ERROR; 170 if (mEnabled) { 171 uint32_t replySize = mCaptureSize; 172 status = command(VISU_CMD_CAPTURE, 0, NULL, &replySize, waveform); 173 LOGV("getWaveForm() command returned %d", status); 174 if (replySize == 0) { 175 status = NOT_ENOUGH_DATA; 176 } 177 } else { 178 LOGV("getWaveForm() disabled"); 179 memset(waveform, 0x80, mCaptureSize); 180 } 181 return status; 182 } 183 184 status_t Visualizer::getFft(uint8_t *fft) 185 { 186 if (fft == NULL) { 187 return BAD_VALUE; 188 } 189 if (mCaptureSize == 0) { 190 return NO_INIT; 191 } 192 193 status_t status = NO_ERROR; 194 if (mEnabled) { 195 uint8_t buf[mCaptureSize]; 196 status = getWaveForm(buf); 197 if (status == NO_ERROR) { 198 status = doFft(fft, buf); 199 } 200 } else { 201 memset(fft, 0, mCaptureSize); 202 } 203 return status; 204 } 205 206 status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform) 207 { 208 int32_t workspace[mCaptureSize >> 1]; 209 int32_t nonzero = 0; 210 211 for (uint32_t i = 0; i < mCaptureSize; i += 2) { 212 workspace[i >> 1] = (waveform[i] ^ 0x80) << 23; 213 workspace[i >> 1] |= (waveform[i + 1] ^ 0x80) << 7; 214 nonzero |= workspace[i >> 1]; 215 } 216 217 if (nonzero) { 218 fixed_fft_real(mCaptureSize >> 1, workspace); 219 } 220 221 for (uint32_t i = 0; i < mCaptureSize; i += 2) { 222 fft[i] = workspace[i >> 1] >> 23; 223 fft[i + 1] = workspace[i >> 1] >> 7; 224 } 225 226 return NO_ERROR; 227 } 228 229 void Visualizer::periodicCapture() 230 { 231 Mutex::Autolock _l(mLock); 232 LOGV("periodicCapture() %p mCaptureCallBack %p mCaptureFlags 0x%08x", 233 this, mCaptureCallBack, mCaptureFlags); 234 if (mCaptureCallBack != NULL && 235 (mCaptureFlags & (CAPTURE_WAVEFORM|CAPTURE_FFT)) && 236 mCaptureSize != 0) { 237 uint8_t waveform[mCaptureSize]; 238 status_t status = getWaveForm(waveform); 239 if (status != NO_ERROR) { 240 return; 241 } 242 uint8_t fft[mCaptureSize]; 243 if (mCaptureFlags & CAPTURE_FFT) { 244 status = doFft(fft, waveform); 245 } 246 if (status != NO_ERROR) { 247 return; 248 } 249 uint8_t *wavePtr = NULL; 250 uint8_t *fftPtr = NULL; 251 uint32_t waveSize = 0; 252 uint32_t fftSize = 0; 253 if (mCaptureFlags & CAPTURE_WAVEFORM) { 254 wavePtr = waveform; 255 waveSize = mCaptureSize; 256 } 257 if (mCaptureFlags & CAPTURE_FFT) { 258 fftPtr = fft; 259 fftSize = mCaptureSize; 260 } 261 mCaptureCallBack(mCaptureCbkUser, waveSize, wavePtr, fftSize, fftPtr, mSampleRate); 262 } 263 } 264 265 uint32_t Visualizer::initCaptureSize() 266 { 267 uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2]; 268 effect_param_t *p = (effect_param_t *)buf32; 269 270 p->psize = sizeof(uint32_t); 271 p->vsize = sizeof(uint32_t); 272 *(int32_t *)p->data = VISU_PARAM_CAPTURE_SIZE; 273 status_t status = getParameter(p); 274 275 if (status == NO_ERROR) { 276 status = p->status; 277 } 278 279 uint32_t size = 0; 280 if (status == NO_ERROR) { 281 size = *((int32_t *)p->data + 1); 282 } 283 mCaptureSize = size; 284 285 LOGV("initCaptureSize size %d status %d", mCaptureSize, status); 286 287 return size; 288 } 289 290 //------------------------------------------------------------------------- 291 292 Visualizer::CaptureThread::CaptureThread(Visualizer& receiver, uint32_t captureRate, bool bCanCallJava) 293 : Thread(bCanCallJava), mReceiver(receiver) 294 { 295 mSleepTimeUs = 1000000000 / captureRate; 296 LOGV("CaptureThread cstor %p captureRate %d mSleepTimeUs %d", this, captureRate, mSleepTimeUs); 297 } 298 299 bool Visualizer::CaptureThread::threadLoop() 300 { 301 LOGV("CaptureThread %p enter", this); 302 while (!exitPending()) 303 { 304 usleep(mSleepTimeUs); 305 mReceiver.periodicCapture(); 306 } 307 LOGV("CaptureThread %p exiting", this); 308 return false; 309 } 310 311 status_t Visualizer::CaptureThread::readyToRun() 312 { 313 return NO_ERROR; 314 } 315 316 void Visualizer::CaptureThread::onFirstRef() 317 { 318 } 319 320 }; // namespace android 321 322