blob: 32cdb491e3871a5d75fe0ef6b519673805a62762 [file] [log] [blame]
/*
**
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "Visualizer"
#include <utils/Log.h>
#include <stdint.h>
#include <sys/types.h>
#include <limits.h>
#include <media/Visualizer.h>
extern "C" {
#define FLOATING_POINT 1
#include "fftwrap.h"
}
namespace android {
// ---------------------------------------------------------------------------
Visualizer::Visualizer (int32_t priority,
effect_callback_t cbf,
void* user,
int sessionId)
: AudioEffect(SL_IID_VISUALIZATION, NULL, priority, cbf, user, sessionId),
mCaptureRate(CAPTURE_RATE_DEF),
mCaptureSize(CAPTURE_SIZE_DEF),
mSampleRate(44100000),
mCaptureCallBack(NULL),
mCaptureCbkUser(NULL)
{
initCaptureSize();
if (mCaptureSize != 0) {
mFftTable = spx_fft_init(mCaptureSize);
} else {
mFftTable = NULL;
}
}
Visualizer::~Visualizer()
{
if (mFftTable != NULL) {
spx_fft_destroy(mFftTable);
}
}
status_t Visualizer::setEnabled(bool enabled)
{
Mutex::Autolock _l(mLock);
sp<CaptureThread> t = mCaptureThread;
if (t != 0) {
if (enabled) {
if (t->exitPending()) {
if (t->requestExitAndWait() == WOULD_BLOCK) {
LOGE("Visualizer::enable() called from thread");
return INVALID_OPERATION;
}
}
}
t->mLock.lock();
}
status_t status = AudioEffect::setEnabled(enabled);
if (status == NO_ERROR) {
if (t != 0) {
if (enabled) {
t->run("AudioTrackThread");
} else {
t->requestExit();
}
}
}
if (t != 0) {
t->mLock.unlock();
}
return status;
}
status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, uint32_t rate)
{
if (rate > CAPTURE_RATE_MAX) {
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
if (mEnabled) {
return INVALID_OPERATION;
}
sp<CaptureThread> t = mCaptureThread;
if (t != 0) {
t->mLock.lock();
}
mCaptureThread.clear();
mCaptureCallBack = cbk;
mCaptureCbkUser = user;
mCaptureFlags = flags;
mCaptureRate = rate;
if (t != 0) {
t->mLock.unlock();
}
if (cbk != NULL) {
mCaptureThread = new CaptureThread(*this, rate, ((flags & CAPTURE_CALL_JAVA) != 0));
if (mCaptureThread == 0) {
LOGE("Could not create callback thread");
return NO_INIT;
}
}
LOGV("setCaptureCallBack() rate: %d thread %p flags 0x%08x",
rate, mCaptureThread.get(), mCaptureFlags);
return NO_ERROR;
}
status_t Visualizer::setCaptureSize(uint32_t size)
{
if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
size < VISUALIZER_CAPTURE_SIZE_MIN ||
AudioSystem::popCount(size) != 1) {
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
if (mEnabled) {
return INVALID_OPERATION;
}
uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
effect_param_t *p = (effect_param_t *)buf32;
p->psize = sizeof(uint32_t);
p->vsize = sizeof(uint32_t);
*(int32_t *)p->data = VISU_PARAM_CAPTURE_SIZE;
*((int32_t *)p->data + 1)= size;
status_t status = setParameter(p);
LOGV("setCaptureSize size %d status %d p->status %d", size, status, p->status);
if (status == NO_ERROR) {
status = p->status;
}
if (status == NO_ERROR) {
mCaptureSize = size;
if (mFftTable != NULL) {
spx_fft_destroy(mFftTable);
}
mFftTable = spx_fft_init(mCaptureSize);
LOGV("setCaptureSize size %d mFftTable %p", mCaptureSize, mFftTable);
}
return status;
}
status_t Visualizer::getWaveForm(uint8_t *waveform)
{
if (waveform == NULL) {
return BAD_VALUE;
}
if (mCaptureSize == 0) {
return NO_INIT;
}
status_t status = NO_ERROR;
if (mEnabled) {
uint32_t replySize = mCaptureSize;
status_t status = command(VISU_CMD_CAPTURE, 0, NULL, &replySize, waveform);
if (replySize == 0) {
status = NOT_ENOUGH_DATA;
}
} else {
memset(waveform, 0x80, mCaptureSize);
}
return status;
}
status_t Visualizer::getFft(uint8_t *fft)
{
if (fft == NULL) {
return BAD_VALUE;
}
if (mCaptureSize == 0) {
return NO_INIT;
}
status_t status = NO_ERROR;
if (mEnabled) {
uint8_t buf[mCaptureSize];
status_t status = getWaveForm(buf);
if (status == NO_ERROR) {
status = doFft(fft, buf);
}
} else {
memset(fft, 0, mCaptureSize);
}
return status;
}
status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
{
if (mFftTable == NULL) {
return NO_INIT;
}
float fsrc[mCaptureSize];
for (uint32_t i = 0; i < mCaptureSize; i++) {
fsrc[i] = (int16_t)(waveform[i] ^ 0x80) << 8;
}
float fdst[mCaptureSize];
spx_fft_float(mFftTable, fsrc, fdst);
for (uint32_t i = 0; i < mCaptureSize; i++) {
fft[i] = (uint8_t)((int32_t)fdst[i] >> 8);
}
return NO_ERROR;
}
void Visualizer::periodicCapture()
{
Mutex::Autolock _l(mLock);
LOGV("periodicCapture() %p mCaptureCallBack %p mCaptureFlags 0x%08x",
this, mCaptureCallBack, mCaptureFlags);
if (mCaptureCallBack != NULL &&
(mCaptureFlags & (CAPTURE_WAVEFORM|CAPTURE_FFT)) &&
mCaptureSize != 0) {
uint8_t waveform[mCaptureSize];
status_t status = getWaveForm(waveform);
if (status != NO_ERROR) {
return;
}
uint8_t fft[mCaptureSize];
if (mCaptureFlags & CAPTURE_FFT) {
status = doFft(fft, waveform);
}
if (status != NO_ERROR) {
return;
}
uint8_t *wavePtr = NULL;
uint8_t *fftPtr = NULL;
uint32_t waveSize = 0;
uint32_t fftSize = 0;
if (mCaptureFlags & CAPTURE_WAVEFORM) {
wavePtr = waveform;
waveSize = mCaptureSize;
}
if (mCaptureFlags & CAPTURE_FFT) {
fftPtr = fft;
fftSize = mCaptureSize;
}
mCaptureCallBack(mCaptureCbkUser, waveSize, wavePtr, fftSize, fftPtr, mSampleRate);
}
}
uint32_t Visualizer::initCaptureSize()
{
uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
effect_param_t *p = (effect_param_t *)buf32;
p->psize = sizeof(uint32_t);
p->vsize = sizeof(uint32_t);
*(int32_t *)p->data = VISU_PARAM_CAPTURE_SIZE;
status_t status = getParameter(p);
if (status == NO_ERROR) {
status = p->status;
}
uint32_t size = 0;
if (status == NO_ERROR) {
size = *((int32_t *)p->data + 1);
}
mCaptureSize = size;
LOGV("initCaptureSize size %d status %d", mCaptureSize, status);
return size;
}
//-------------------------------------------------------------------------
Visualizer::CaptureThread::CaptureThread(Visualizer& receiver, uint32_t captureRate, bool bCanCallJava)
: Thread(bCanCallJava), mReceiver(receiver)
{
mSleepTimeUs = 1000000000 / captureRate;
LOGV("CaptureThread cstor %p captureRate %d mSleepTimeUs %d", this, captureRate, mSleepTimeUs);
}
bool Visualizer::CaptureThread::threadLoop()
{
LOGV("CaptureThread %p enter", this);
while (!exitPending())
{
usleep(mSleepTimeUs);
mReceiver.periodicCapture();
}
LOGV("CaptureThread %p exiting", this);
return false;
}
status_t Visualizer::CaptureThread::readyToRun()
{
return NO_ERROR;
}
void Visualizer::CaptureThread::onFirstRef()
{
}
}; // namespace android