summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Laurent <elaurent@google.com>2010-07-02 08:12:41 -0700
committerEric Laurent <elaurent@google.com>2010-07-07 11:00:28 -0700
commitdf9b81ced437b11f8a3fcf4ba3ea6af703d121e2 (patch)
treec80b989df744ea74f62c800a8848813174792cc2
parent31e0ffe8444b70500cac319da084c4c45e62aca2 (diff)
downloadframeworks_base-df9b81ced437b11f8a3fcf4ba3ea6af703d121e2.zip
frameworks_base-df9b81ced437b11f8a3fcf4ba3ea6af703d121e2.tar.gz
frameworks_base-df9b81ced437b11f8a3fcf4ba3ea6af703d121e2.tar.bz2
Added Visualizer effect.
The visualizer enables application to retrieve part of the currently playing audio for visualization purpose. It is not an audio recording interface and only returns partial and low quality audio content as a waveform or a frequency representation (FFT). Removed temporary hack made in MediaPlayer for animated wall papers based on audio visualization (snoop() method. This commit also includes a change in AudioEffect class: - the enable()/disable() methods have been replaced bya more standard setEnabled() method. - some fixes in javadoc Change-Id: Id092a1340e9e38dae68646ade7be054e3a36980e
-rw-r--r--include/media/AudioEffect.h70
-rw-r--r--include/media/EffectVisualizerApi.h55
-rw-r--r--include/media/IMediaPlayerService.h4
-rw-r--r--include/media/Visualizer.h160
-rw-r--r--include/media/mediaplayer.h1
-rw-r--r--libs/audioflinger/AudioFlinger.cpp40
-rw-r--r--libs/audioflinger/AudioFlinger.h6
-rw-r--r--media/java/android/media/AudioEffect.java713
-rwxr-xr-xmedia/java/android/media/Visualizer.java510
-rw-r--r--media/jni/android_media_MediaPlayer.cpp13
-rw-r--r--media/jni/audioeffect/Android.mk3
-rw-r--r--media/jni/audioeffect/android_media_AudioEffect.cpp41
-rw-r--r--media/jni/audioeffect/android_media_Visualizer.cpp507
-rw-r--r--media/libeffects/Android.mk30
-rw-r--r--media/libeffects/EffectVisualizer.cpp401
-rw-r--r--media/libmedia/Android.mk3
-rw-r--r--media/libmedia/AudioEffect.cpp36
-rw-r--r--media/libmedia/IMediaPlayerService.cpp17
-rw-r--r--media/libmedia/Visualizer.cpp330
-rw-r--r--media/libmedia/mediaplayer.cpp57
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp121
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.h4
22 files changed, 2499 insertions, 623 deletions
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index 66670f3..e9ff8a3 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -307,29 +307,18 @@ public:
int32_t priority() const { return mPriority; }
- /* Enables the effect engine.
+ /* Enables or disables the effect engine.
*
* Parameters:
- * None.
+ * enabled: requested enable state.
*
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful operation
- * - INVALID_OPERATION: the application does not have control of the effect engine
+ * - INVALID_OPERATION: the application does not have control of the effect engine or the
+ * effect is already in the requested state.
*/
- status_t enable();
-
- /* Disables the effect engine.
- *
- * Parameters:
- * None.
- *
- * Returned status (from utils/Errors.h) can be:
- * - NO_ERROR: successful operation
- * - INVALID_OPERATION: the application does not have control of the effect engine
- */
- status_t disable();
-
- bool isEnabled() const;
+ virtual status_t setEnabled(bool enabled);
+ bool getEnabled() const;
/* Sets a parameter value.
*
@@ -342,7 +331,7 @@ public:
* - BAD_VALUE: invalid parameter identifier or value.
* - DEAD_OBJECT: the effect engine has been deleted.
*/
- status_t setParameter(effect_param_t *param);
+ virtual status_t setParameter(effect_param_t *param);
/* Prepare a new parameter value that will be set by next call to
* setParameterCommit(). This method can be used to set multiple parameters
@@ -359,7 +348,7 @@ public:
* - NO_MEMORY: no more space available in shared memory used for deferred parameter
* setting.
*/
- status_t setParameterDeferred(effect_param_t *param);
+ virtual status_t setParameterDeferred(effect_param_t *param);
/* Commit all parameter values previously prepared by setParameterDeferred().
*
@@ -373,7 +362,7 @@ public:
* as to which of the parameters caused this error.
* - DEAD_OBJECT: the effect engine has been deleted.
*/
- status_t setParameterCommit();
+ virtual status_t setParameterCommit();
/* Gets a parameter value.
*
@@ -387,13 +376,17 @@ public:
* - BAD_VALUE: invalid parameter identifier.
* - DEAD_OBJECT: the effect engine has been deleted.
*/
- status_t getParameter(effect_param_t *param);
+ virtual status_t getParameter(effect_param_t *param);
/* Sends a command and receives a response to/from effect engine.
* See EffectApi.h for details on effect command() function, valid command codes
* and formats.
*/
- status_t command(int32_t cmdCode, int32_t cmdSize, void *cmdData, int32_t *replySize, void *replyData);
+ virtual status_t command(int32_t cmdCode,
+ int32_t cmdSize,
+ void *cmdData,
+ int32_t *replySize,
+ void *replyData);
/*
@@ -409,6 +402,17 @@ public:
*/
static status_t guidToString(const effect_uuid_t *guid, char *str, size_t maxLen);
+protected:
+ volatile int32_t mEnabled; // enable state
+ int32_t mSessionId; // audio session ID
+ int32_t mPriority; // priority for effect control
+ status_t mStatus; // effect status
+ effect_callback_t mCbf; // callback function for status, control and
+ // parameter changes notifications
+ void* mUserData; // client context for callback function
+ effect_descriptor_t mDescriptor; // effect descriptor
+ int32_t mId; // system wide unique effect engine instance ID
+
private:
// Implements the IEffectClient interface
@@ -419,9 +423,17 @@ private:
EffectClient(AudioEffect *effect) : mEffect(effect){}
// IEffectClient
- virtual void controlStatusChanged(bool controlGranted) {mEffect->controlStatusChanged(controlGranted);}
- virtual void enableStatusChanged(bool enabled) {mEffect->enableStatusChanged(enabled);}
- virtual void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData) {
+ virtual void controlStatusChanged(bool controlGranted) {
+ mEffect->controlStatusChanged(controlGranted);
+ }
+ virtual void enableStatusChanged(bool enabled) {
+ mEffect->enableStatusChanged(enabled);
+ }
+ virtual void commandExecuted(int cmdCode,
+ int cmdSize,
+ void *pCmdData,
+ int replySize,
+ void *pReplyData) {
mEffect->commandExecuted(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
}
@@ -446,14 +458,6 @@ private:
sp<EffectClient> mIEffectClient; // IEffectClient implementation
sp<IMemory> mCblkMemory; // shared memory for deferred parameter setting
effect_param_cblk_t* mCblk; // control block for deferred parameter setting
- int32_t mPriority; // priority for effect control
- status_t mStatus; // effect status
- volatile int32_t mEnabled; // enable state
- effect_callback_t mCbf; // callback function for status, control, parameter changes notifications
- void* mUserData; // client context for callback function
- effect_descriptor_t mDescriptor; // effect descriptor
- int32_t mId; // system wide unique effect engine instance identifier
- int32_t mSessionId; // audio session ID
};
diff --git a/include/media/EffectVisualizerApi.h b/include/media/EffectVisualizerApi.h
new file mode 100644
index 0000000..1155db8
--- /dev/null
+++ b/include/media/EffectVisualizerApi.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_EFFECTVISUALIZERAPI_H_
+#define ANDROID_EFFECTVISUALIZERAPI_H_
+
+#include <media/EffectApi.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+//TODO replace by openSL ES include when available
+static const effect_uuid_t SL_IID_VISUALIZATION_ =
+ { 0xe46b26a0, 0xdddd, 0x11db, 0x8afd, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
+const effect_uuid_t * const SL_IID_VISUALIZATION = &SL_IID_VISUALIZATION_;
+
+#define VISUALIZER_CAPTURE_SIZE_MAX 1024 // maximum capture size in samples
+#define VISUALIZER_CAPTURE_SIZE_MIN 128 // minimum capture size in samples
+
+/* enumerated parameters for Visualizer effect */
+typedef enum
+{
+ VISU_PARAM_CAPTURE_SIZE, // Sets the number PCM samples in the capture.
+} t_visualizer_params;
+
+/* commands */
+typedef enum
+{
+ VISU_CMD_CAPTURE = EFFECT_CMD_FIRST_PROPRIETARY, // Gets the latest PCM capture.
+}t_visualizer_cmds;
+
+// VISU_CMD_CAPTURE retrieves the latest PCM snapshot captured by the visualizer engine.
+// It returns the number of samples specified by VISU_PARAM_CAPTURE_SIZE
+// in 8 bit unsigned format (0 = 0x80)
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+
+#endif /*ANDROID_EFFECTVISUALIZERAPI_H_*/
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index e892875..9416ca1 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -48,10 +48,6 @@ public:
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
virtual sp<IOMX> getOMX() = 0;
-
- // Take a peek at currently playing audio, for visualization purposes.
- // This returns a buffer of 16 bit mono PCM data, or NULL if no visualization buffer is currently available.
- virtual sp<IMemory> snoop() = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/Visualizer.h b/include/media/Visualizer.h
new file mode 100644
index 0000000..5d51de8
--- /dev/null
+++ b/include/media/Visualizer.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_MEDIA_VISUALIZER_H
+#define ANDROID_MEDIA_VISUALIZER_H
+
+#include <media/AudioEffect.h>
+#include <media/EffectVisualizerApi.h>
+#include <string.h>
+
+/**
+ * The Visualizer class enables application to retrieve part of the currently playing audio for
+ * visualization purpose. It is not an audio recording interface and only returns partial and low
+ * quality audio content. However, to protect privacy of certain audio data (e.g voice mail) the use
+ * of the visualizer requires the permission android.permission.RECORD_AUDIO.
+ * The audio session ID passed to the constructor indicates which audio content should be
+ * visualized:
+ * - If the session is 0, the audio output mix is visualized
+ * - If the session is not 0, the audio from a particular MediaPlayer or AudioTrack
+ * using this audio session is visualized
+ * Two types of representation of audio content can be captured:
+ * - Waveform data: consecutive 8-bit (unsigned) mono samples by using the getWaveForm() method
+ * - Frequency data: 8-bit magnitude FFT by using the getFft() method
+ *
+ * The length of the capture can be retrieved or specified by calling respectively
+ * getCaptureSize() and setCaptureSize() methods. Note that the size of the FFT
+ * is half of the specified capture size but both sides of the spectrum are returned yielding in a
+ * number of bytes equal to the capture size. The capture size must be a power of 2 in the range
+ * returned by getMinCaptureSize() and getMaxCaptureSize().
+ * In addition to the polling capture mode, a callback mode is also available by installing a
+ * callback function by use of the setCaptureCallBack() method. The rate at which the callback
+ * is called as well as the type of data returned is specified.
+ * Before capturing data, the Visualizer must be enabled by calling the setEnabled() method.
+ * When data capture is not needed any more, the Visualizer should be disabled.
+ */
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class Visualizer: public AudioEffect {
+public:
+
+ enum callback_flags {
+ CAPTURE_WAVEFORM = 0x00000001, // capture callback returns a PCM wave form
+ CAPTURE_FFT = 0x00000002, // apture callback returns a frequency representation
+ CAPTURE_CALL_JAVA = 0x00000004 // the callback thread can call java
+ };
+
+
+ /* Constructor.
+ * See AudioEffect constructor for details on parameters.
+ */
+ Visualizer(int32_t priority = 0,
+ effect_callback_t cbf = 0,
+ void* user = 0,
+ int sessionId = 0);
+
+ ~Visualizer();
+
+ virtual status_t setEnabled(bool enabled);
+
+ // maximum capture size in samples
+ static uint32_t getMaxCaptureSize() { return VISUALIZER_CAPTURE_SIZE_MAX; }
+ // minimum capture size in samples
+ static uint32_t getMinCaptureSize() { return VISUALIZER_CAPTURE_SIZE_MIN; }
+ // maximum capture rate in millihertz
+ static uint32_t getMaxCaptureRate() { return CAPTURE_RATE_MAX; }
+
+ // callback used to return periodic PCM or FFT captures to the application. Either one or both
+ // types of data are returned (PCM and FFT) according to flags indicated when installing the
+ // callback. When a type of data is not present, the corresponding size (waveformSize or
+ // fftSize) is 0.
+ typedef void (*capture_cbk_t)(void* user,
+ uint32_t waveformSize,
+ uint8_t *waveform,
+ uint32_t fftSize,
+ uint8_t *fft,
+ uint32_t samplingrate);
+
+ // install a callback to receive periodic captures. The capture rate is specified in milliHertz
+ // and the capture format is according to flags (see callback_flags).
+ status_t setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, uint32_t rate);
+
+ // set the capture size capture size must be a power of two in the range
+ // [VISUALIZER_CAPTURE_SIZE_MAX. VISUALIZER_CAPTURE_SIZE_MIN]
+ // must be called when the visualizer is not enabled
+ status_t setCaptureSize(uint32_t size);
+ uint32_t getCaptureSize() { return mCaptureSize; }
+
+ // returns the capture rate indicated when installing the callback
+ uint32_t getCaptureRate() { return mCaptureRate; }
+
+ // returns the sampling rate of the audio being captured
+ uint32_t getSamplingRate() { return mSampleRate; }
+
+ // return a capture in PCM 8 bit unsigned format. The size of the capture is equal to
+ // getCaptureSize()
+ status_t getWaveForm(uint8_t *waveform);
+
+ // return a capture in FFT 8 bit signed format. The size of the capture is equal to
+ // getCaptureSize() but the length of the FFT is half of the size (both parts of the spectrum
+ // are returned
+ status_t getFft(uint8_t *fft);
+
+private:
+
+ static const uint32_t CAPTURE_RATE_MAX = 20000;
+ static const uint32_t CAPTURE_RATE_DEF = 10000;
+ static const uint32_t CAPTURE_SIZE_DEF = VISUALIZER_CAPTURE_SIZE_MAX;
+
+ /* internal class to handle the callback */
+ class CaptureThread : public Thread
+ {
+ public:
+ CaptureThread(Visualizer& receiver, uint32_t captureRate, bool bCanCallJava = false);
+
+ private:
+ friend class Visualizer;
+ virtual bool threadLoop();
+ virtual status_t readyToRun();
+ virtual void onFirstRef();
+ Visualizer& mReceiver;
+ Mutex mLock;
+ uint32_t mSleepTimeUs;
+ };
+
+ status_t doFft(uint8_t *fft, uint8_t *waveform);
+ void periodicCapture();
+ uint32_t initCaptureSize();
+
+ Mutex mLock;
+ uint32_t mCaptureRate;
+ uint32_t mCaptureSize;
+ uint32_t mSampleRate;
+ capture_cbk_t mCaptureCallBack;
+ void *mCaptureCbkUser;
+ sp<CaptureThread> mCaptureThread;
+ uint32_t mCaptureFlags;
+ void *mFftTable;
+};
+
+
+}; // namespace android
+
+#endif // ANDROID_MEDIA_VISUALIZER_H
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 62a4e50..4963f73 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -166,7 +166,6 @@ public:
void notify(int msg, int ext1, int ext2);
static sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
static sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
- static int snoop(short *data, int len, int kind);
status_t invoke(const Parcel& request, Parcel *reply);
status_t setMetadataFilter(const Parcel& filter);
status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata);
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 48c04a6..e6f46ce 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -17,7 +17,8 @@
#define LOG_TAG "AudioFlinger"
-//#define LOG_NDEBUG 0
+//
+#define LOG_NDEBUG 0
#include <math.h>
#include <signal.h>
@@ -52,6 +53,7 @@
#endif
#include <media/EffectsFactoryApi.h>
+#include <media/EffectVisualizerApi.h>
// ----------------------------------------------------------------------------
// the sim build doesn't have gettid
@@ -4498,6 +4500,11 @@ status_t AudioFlinger::getEffectDescriptor(effect_uuid_t *pUuid, effect_descript
return EffectGetDescriptor(pUuid, descriptor);
}
+
+// this UUID must match the one defined in media/libeffects/EffectVisualizer.cpp
+static const effect_uuid_t VISUALIZATION_UUID_ =
+ {0xd069d9e0, 0x8329, 0x11df, 0x9168, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+
sp<IEffect> AudioFlinger::createEffect(pid_t pid,
effect_descriptor_t *pDesc,
const sp<IEffectClient>& effectClient,
@@ -4525,6 +4532,15 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
{
Mutex::Autolock _l(mLock);
+ // check recording permission for visualizer
+ if (memcmp(&pDesc->type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0 ||
+ memcmp(&pDesc->uuid, &VISUALIZATION_UUID_, sizeof(effect_uuid_t)) == 0) {
+ if (!recordingAllowed()) {
+ lStatus = PERMISSION_DENIED;
+ goto Exit;
+ }
+ }
+
if (!EffectIsNullUuid(&pDesc->uuid)) {
// if uuid is specified, request effect descriptor
lStatus = EffectGetDescriptor(&pDesc->uuid, &desc);
@@ -5089,7 +5105,7 @@ void AudioFlinger::EffectModule::process()
if (mState != ACTIVE) {
switch (mState) {
case RESET:
- reset();
+ reset_l();
mState = STARTING;
// clear auxiliary effect input buffer for next accumulation
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
@@ -5097,14 +5113,14 @@ void AudioFlinger::EffectModule::process()
}
return;
case STARTING:
- start();
+ start_l();
mState = ACTIVE;
break;
case STOPPING:
mState = STOPPED;
break;
case STOPPED:
- stop();
+ stop_l();
mState = IDLE;
return;
}
@@ -5132,7 +5148,7 @@ void AudioFlinger::EffectModule::process()
}
}
-void AudioFlinger::EffectModule::reset()
+void AudioFlinger::EffectModule::reset_l()
{
if (mEffectInterface == NULL) {
return;
@@ -5205,6 +5221,7 @@ status_t AudioFlinger::EffectModule::configure()
status_t AudioFlinger::EffectModule::init()
{
+ Mutex::Autolock _l(mLock);
if (mEffectInterface == NULL) {
return NO_INIT;
}
@@ -5217,7 +5234,7 @@ status_t AudioFlinger::EffectModule::init()
return status;
}
-status_t AudioFlinger::EffectModule::start()
+status_t AudioFlinger::EffectModule::start_l()
{
if (mEffectInterface == NULL) {
return NO_INIT;
@@ -5231,7 +5248,7 @@ status_t AudioFlinger::EffectModule::start()
return status;
}
-status_t AudioFlinger::EffectModule::stop()
+status_t AudioFlinger::EffectModule::stop_l()
{
if (mEffectInterface == NULL) {
return NO_INIT;
@@ -5247,7 +5264,8 @@ status_t AudioFlinger::EffectModule::stop()
status_t AudioFlinger::EffectModule::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData)
{
- LOGV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface);
+ Mutex::Autolock _l(mLock);
+// LOGV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface);
if (mEffectInterface == NULL) {
return NO_INIT;
@@ -5255,7 +5273,6 @@ status_t AudioFlinger::EffectModule::command(int cmdCode, int cmdSize, void *pCm
status_t status = (*mEffectInterface)->command(mEffectInterface, cmdCode, cmdSize, pCmdData, replySize, pReplyData);
if (cmdCode != EFFECT_CMD_GET_PARAM && status == NO_ERROR) {
int size = (replySize == NULL) ? 0 : *replySize;
- Mutex::Autolock _l(mLock);
for (size_t i = 1; i < mHandles.size(); i++) {
sp<EffectHandle> h = mHandles[i].promote();
if (h != 0) {
@@ -5322,6 +5339,7 @@ bool AudioFlinger::EffectModule::isEnabled()
status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller)
{
+ Mutex::Autolock _l(mLock);
status_t status = NO_ERROR;
// Send volume indication if EFFECT_FLAG_VOLUME_IND is set and read back altered volume
@@ -5347,6 +5365,7 @@ status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right,
status_t AudioFlinger::EffectModule::setDevice(uint32_t device)
{
+ Mutex::Autolock _l(mLock);
status_t status = NO_ERROR;
if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) {
// convert device bit field from AudioSystem to EffectApi format.
@@ -5366,6 +5385,7 @@ status_t AudioFlinger::EffectModule::setDevice(uint32_t device)
status_t AudioFlinger::EffectModule::setMode(uint32_t mode)
{
+ Mutex::Autolock _l(mLock);
status_t status = NO_ERROR;
if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_MODE_MASK) == EFFECT_FLAG_AUDIO_MODE_IND) {
// convert audio mode from AudioSystem to EffectApi format.
@@ -5586,7 +5606,7 @@ void AudioFlinger::EffectHandle::disconnect()
status_t AudioFlinger::EffectHandle::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData)
{
- LOGV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p", cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get());
+// LOGV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p", cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get());
// only get parameter command is permitted for applications not controlling the effect
if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) {
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index 42dca4c..ec3d7f1 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -916,7 +916,7 @@ private:
void process();
status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData);
- void reset();
+ void reset_l();
status_t configure();
status_t init();
uint32_t state() {
@@ -951,8 +951,8 @@ private:
EffectModule(const EffectModule&);
EffectModule& operator = (const EffectModule&);
- status_t start();
- status_t stop();
+ status_t start_l();
+ status_t stop_l();
// update this table when AudioSystem::audio_devices or audio_device_e (in EffectApi.h) are modified
static const uint32_t sDeviceConvTable[];
diff --git a/media/java/android/media/AudioEffect.java b/media/java/android/media/AudioEffect.java
index b1b7fed..053cc22 100644
--- a/media/java/android/media/AudioEffect.java
+++ b/media/java/android/media/AudioEffect.java
@@ -27,22 +27,25 @@ import java.nio.ByteBuffer;
import java.util.UUID;
/**
- * AudioEffect is the base class for implementing audio effect control in Java applications.
- * Creating an AudioEffect object will create the effect engine in audio framework if no
- * instance of the same effect type exists in the specified audio session.
- * If one exists, this instance will be used. The application creating the AudioEffect object
- * (or a derived class) will either receive control of the effect engine or not depending
- * on the priority parameter. If priority is higher than the priority used by the current
- * effect engine owner, the control will be transfered to the new object. Otherwise
- * control will remain with the previous object. In this case, the new application will be
- * notified of changes in effect engine state or control ownership by the appropiate listener.
- * If the effect is to be applied to a specific AudioTrack or MediaPlayer instance,
- * the application must specify the audio session ID of that instance.
+ * AudioEffect is the base class for implementing audio effect control in Java
+ * applications.
+ * <p>Creating an AudioEffect object will create the effect engine in
+ * audio framework if no instance of the same effect type exists in the
+ * specified audio session. If one exists, this instance will be used.
+ * <p>The application creating the AudioEffect object (or a derived class) will either
+ * receive control of the effect engine or not depending on the priority
+ * parameter. If priority is higher than the priority used by the current effect
+ * engine owner, the control will be transfered to the new object. Otherwise
+ * control will remain with the previous object. In this case, the new
+ * application will be notified of changes in effect engine state or control
+ * ownership by the appropiate listener.
+ * <p>If the effect is to be applied to a specific AudioTrack or MediaPlayer instance,
+ * the application must specify the audio session ID of that instance when calling the AudioEffect
+ * constructor.
*
- * {@hide Pending API council review}
+ * { @hide Pending API council review }
*/
-public class AudioEffect
-{
+public class AudioEffect {
static {
System.loadLibrary("audioeffect_jni");
native_init();
@@ -51,31 +54,60 @@ public class AudioEffect
private final static String TAG = "AudioEffect-JAVA";
/**
- * The following UUIDs define effect types corresponding to standard audio effects
- * whose implementation and interface conform to the OpenSL ES specification.
- * The definitions match the corresponding interface IDs in OpenSLES_IID.h
+ * The following UUIDs define effect types corresponding to standard audio
+ * effects whose implementation and interface conform to the OpenSL ES
+ * specification. The definitions match the corresponding interface IDs in
+ * OpenSLES_IID.h
*/
- public static final UUID EFFECT_TYPE_ENV_REVERB = UUID.fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e");
- public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID.fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b");
- public static final UUID EFFECT_TYPE_EQUALIZER = UUID.fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b");
- public static final UUID EFFECT_TYPE_BASS_BOOST = UUID.fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b");
- public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID.fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b");
- public static final UUID EFFECT_TYPE_INVALID = UUID.fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210");
+ /**
+ * UUID for environmental reverb effect
+ */
+ public static final UUID EFFECT_TYPE_ENV_REVERB = UUID
+ .fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e");
+ /**
+ * UUID for preset reverb effect
+ */
+ public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID
+ .fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b");
+ /**
+ * UUID for equalizer effect
+ */
+ public static final UUID EFFECT_TYPE_EQUALIZER = UUID
+ .fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b");
+ /**
+ * UUID for bass boost effect
+ */
+ public static final UUID EFFECT_TYPE_BASS_BOOST = UUID
+ .fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b");
+ /**
+ * UUID for virtualizer effect
+ */
+ public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID
+ .fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b");
/**
- * State of an AudioEffect object that was not successfully initialized upon creation
+ * Null effect UUID. Used when the UUID for effect type of
+ */
+ public static final UUID EFFECT_TYPE_NULL = UUID
+ .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210");
+
+ /**
+ * State of an AudioEffect object that was not successfully initialized upon
+ * creation
*/
public static final int STATE_UNINITIALIZED = 0;
/**
* State of an AudioEffect object that is ready to be used.
*/
- public static final int STATE_INITIALIZED = 1;
+ public static final int STATE_INITIALIZED = 1;
+ // to keep in sync with
+ // frameworks/base/media/jni/audioeffect/android_media_AudioEffect.cpp
/**
* Event id for engine state change notification.
*/
- protected static final int NATIVE_EVENT_ENABLED_STATUS = 0;
+ protected static final int NATIVE_EVENT_ENABLED_STATUS = 0;
/**
* Event id for engine control ownership change notification.
*/
@@ -85,56 +117,89 @@ public class AudioEffect
*/
protected static final int NATIVE_EVENT_PARAMETER_CHANGED = 2;
+ /**
+ * Successful operation.
+ */
+ public static final int SUCCESS = 0;
+ /**
+ * Unspecified error.
+ */
+ public static final int ERROR = -1;
+ /**
+ * Internal opreation status. Not returned by any method.
+ */
+ public static final int ALREADY_EXISTS = -2;
+ /**
+ * Operation failed due to bad object initialization.
+ */
+ public static final int ERROR_NO_INIT = -3;
+ /**
+ * Operation failed due to bad parameter value.
+ */
+ public static final int ERROR_BAD_VALUE = -4;
+ /**
+ * Operation failed because it was requested in wrong state.
+ */
+ public static final int ERROR_INVALID_OPERATION = -5;
+ /**
+ * Operation failed due to lack of memory.
+ */
+ public static final int ERROR_NO_MEMORY = -6;
+ /**
+ * Operation failed due to dead remote object.
+ */
+ public static final int ERROR_DEAD_OBJECT = -7;
+
+ /**
+ * The effect descriptor contains necessary information to facilitate
+ * effects enumeration:<br>
+ * <ul>
+ * <li>mType: UUID corresponding to the OpenSL ES interface implemented by this effect</li>
+ * <li>mUuid: UUID for this particular implementation</li>
+ * <li>mConnectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li>
+ * <li>mName: human readable effect name</li>
+ * <li>mImplementor: human readable effect implementor name</li>
+ * </ul>
+ */
+ public static class Descriptor {
+
+ public Descriptor() {
+ }
+
+ public Descriptor(String type, String uuid, String connectMode,
+ String name, String implementor) {
+ mType = UUID.fromString(type);
+ mUuid = UUID.fromString(uuid);
+ mConnectMode = connectMode;
+ mName = name;
+ mImplementor = implementor;
+ }
+
+ public UUID mType;
+ public UUID mUuid;
+ public String mConnectMode;
+ public String mName;
+ public String mImplementor;
+ };
+
+ /**
+ * Effect connection mode is insert. Specifying an audio session ID when creating the effect
+ * will insert this effect after all players in the same audio session.
+ */
+ public static final String EFFECT_INSERT = "Insert";
+ /**
+ * Effect connection mode is auxiliary.
+ * <p>Auxiliary effects must be created on session 0 (global output mix). In order for a
+ * MediaPlayer or AudioTrack to be fed into this effect, they must be explicitely attached to
+ * this effect and a send level must be specified.
+ * <p>Use the effect ID returned by {@link #getId()} to designate this particular effect when
+ * attaching it to the MediaPlayer or AudioTrack.
+ */
+ public static final String EFFECT_AUXILIARY = "Auxiliary";
- // to keep in sync with frameworks/base/media/jni/audioeffect/android_media_AudioEffect.cpp
- public static final int SUCCESS = 0;
- public static final int ERROR = -1;
- public static final int ALREADY_EXISTS = -2;
- public static final int NO_INIT = -3;
- public static final int BAD_VALUE = -4;
- public static final int INVALID_OPERATION = -5;
- public static final int NO_MEMORY = -6;
- public static final int DEAD_OBJECT = -7;
-
-
- /**
- * The effect descriptor contains necessary information to facilitate
- * effects enumeration:
- * mType: UUID corresponding to the OpenSL ES interface implemented by this effect
- * mUuid: UUID for this particular implementation
- * mConnectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}
- * mName: human readable effect name
- * mImplementor: human readable effect implementor name
- */
- public static class Descriptor {
-
- public Descriptor() {
- }
- public Descriptor(String type,
- String uuid,
- String connectMode,
- String name,
- String implementor) {
- mType = UUID.fromString(type);
- mUuid = UUID.fromString(uuid);
- mConnectMode = connectMode;
- mName = name;
- mImplementor = implementor;
- }
-
- public UUID mType;
- public UUID mUuid;
- public String mConnectMode;
- public String mName;
- public String mImplementor;
- };
-
- public static final String EFFECT_INSERT = "Insert";
- public static final String EFFECT_AUXILIARY = "Auxiliary";
-
- //--------------------------------------------------------------------------
+ // --------------------------------------------------------------------------
// Member variables
- //--------------------
+ // --------------------
/**
* Indicates the state of the AudioEffect instance
*/
@@ -159,17 +224,20 @@ public class AudioEffect
/**
* Listener for effect engine state change notifications.
- * @see #setEnableStatusListener(OnEnableStatusChangeListener)
+ *
+ * @see #setEnableStatusListener(OnEnableStatusChangeListener)
*/
protected OnEnableStatusChangeListener mEnableStatusChangeListener = null;
/**
* Listener for effect engine control ownership change notifications.
- * @see #setControlStatusListener(OnControlStatusChangeListener)
+ *
+ * @see #setControlStatusListener(OnControlStatusChangeListener)
*/
protected OnControlStatusChangeListener mControlChangeStatusListener = null;
/**
* Listener for effect engine control ownership change notifications.
- * @see #setParameterListener(OnParameterChangeListener)
+ *
+ * @see #setParameterListener(OnParameterChangeListener)
*/
protected OnParameterChangeListener mParameterChangeListener = null;
/**
@@ -181,32 +249,36 @@ public class AudioEffect
*/
protected NativeEventHandler mNativeEventHandler = null;
-
-
- //--------------------------------------------------------------------------
+ // --------------------------------------------------------------------------
// Constructor, Finalize
- //--------------------
+ // --------------------
/**
* Class constructor.
- * @param type: type of effect engine created. See
- * {@link #EFFECT_TYPE_ENV_REVERB}, {@link #EFFECT_TYPE_EQUALIZER} ...
- * Types corresponding to built-in effects are defined by AudioEffect class.
- * Other types can be specified provided they correspond an existing OpenSL ES
- * interface ID and the corresponsing effect is available on the platform.
- * If an unspecified effect type is requested, the constructor with throw the
- * IllegalArgumentException.
- * @param uuid: unique identifier of a particular effect implementation. Must be
- * specified if the caller wants to use a particular implementation of an effect type.
- * This parameter can be set to null in which case only the type will be used to select
- * the effect.
- * @param priority: the priority level requested by the application for controlling
- * the effect engine. As the same effect engine can be shared by several applications,
- * this parameter indicates how much the requesting application needs control of
- * effect parameters. The normal priority is 0, above normal is a positive number,
- * below normal a negative number.
- * @param audioSession: System wide unique audio session identifier. If audioSession
- * is not 0, the effect will be attached to the MediaPlayer or AudioTrack in the
- * same audio session. Otherwise, the effect will apply to the output mix.
+ *
+ * @param type type of effect engine created. See {@link #EFFECT_TYPE_ENV_REVERB},
+ * {@link #EFFECT_TYPE_EQUALIZER} ... Types corresponding to
+ * built-in effects are defined by AudioEffect class. Other types
+ * can be specified provided they correspond an existing OpenSL
+ * ES interface ID and the corresponsing effect is available on
+ * the platform. If an unspecified effect type is requested, the
+ * constructor with throw the IllegalArgumentException. This
+ * parameter can be set to {@link #EFFECT_TYPE_NULL} in which
+ * case only the uuid will be used to select the effect.
+ * @param uuid unique identifier of a particular effect implementation.
+ * Must be specified if the caller wants to use a particular
+ * implementation of an effect type. This parameter can be set to
+ * {@link #EFFECT_TYPE_NULL} in which case only the type will
+ * be used to select the effect.
+ * @param priority the priority level requested by the application for
+ * controlling the effect engine. As the same effect engine can
+ * be shared by several applications, this parameter indicates
+ * how much the requesting application needs control of effect
+ * parameters. The normal priority is 0, above normal is a
+ * positive number, below normal a negative number.
+ * @param audioSession System wide unique audio session identifier. If audioSession
+ * is not 0, the effect will be attached to the MediaPlayer or
+ * AudioTrack in the same audio session. Otherwise, the effect
+ * will apply to the output mix.
*
* @throws java.lang.IllegalArgumentException
* @throws java.lang.UnsupportedOperationException
@@ -214,22 +286,28 @@ public class AudioEffect
*/
public AudioEffect(UUID type, UUID uuid, int priority, int audioSession)
- throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
+ throws IllegalArgumentException, UnsupportedOperationException,
+ RuntimeException {
int[] id = new int[1];
Descriptor[] desc = new Descriptor[1];
// native initialization
int initResult = native_setup(new WeakReference<AudioEffect>(this),
- type.toString(), uuid.toString(), priority, audioSession, id, desc);
+ type.toString(), uuid.toString(), priority, audioSession, id,
+ desc);
if (initResult != SUCCESS && initResult != ALREADY_EXISTS) {
- Log.e(TAG, "Error code "+initResult+" when initializing AudioEffect.");
+ Log.e(TAG, "Error code " + initResult
+ + " when initializing AudioEffect.");
switch (initResult) {
- case BAD_VALUE:
- throw (new IllegalArgumentException("Effect type: "+type+ " not supported."));
- case INVALID_OPERATION:
- throw (new UnsupportedOperationException("Effect library not loaded"));
+ case ERROR_BAD_VALUE:
+ throw (new IllegalArgumentException("Effect type: " + type
+ + " not supported."));
+ case ERROR_INVALID_OPERATION:
+ throw (new UnsupportedOperationException(
+ "Effect library not loaded"));
default:
- throw (new RuntimeException("Cannot initialize effect engine for type: "+type+
- "Error: "+ initResult));
+ throw (new RuntimeException(
+ "Cannot initialize effect engine for type: " + type
+ + "Error: " + initResult));
}
}
mId = id[0];
@@ -240,9 +318,9 @@ public class AudioEffect
}
/**
- * Releases the native AudioEffect resources. It is a good practice to release the
- * effect engine when not in use as control can be returned to other applications
- * or the native resources released.
+ * Releases the native AudioEffect resources. It is a good practice to
+ * release the effect engine when not in use as control can be returned to
+ * other applications or the native resources released.
*/
public void release() {
synchronized (mStateLock) {
@@ -258,119 +336,115 @@ public class AudioEffect
/**
* Get the effect descriptor.
- * {@see #Descriptor}.
+ *
+ //TODO when AudioEffect class is unhidden @ see android.media.AudioEffect.Descriptor
* @throws IllegalStateException
*/
- public Descriptor getDescriptor()
- throws IllegalStateException {
+ public Descriptor getDescriptor() throws IllegalStateException {
checkState("getDescriptor()");
return mDescriptor;
}
- //--------------------------------------------------------------------------
+ // --------------------------------------------------------------------------
// Effects Enumeration
- //--------------------
+ // --------------------
/**
* Query all effects available on the platform. Returns an array of
- * {@link #Descriptor} objects
+ //TODO when AudioEffect class is unhidden: {@ link android.media.AudioEffect.Descriptor} objects
*
* @throws IllegalStateException
*/
static public Descriptor[] queryEffects() {
- return (Descriptor[])native_query_effects();
+ return (Descriptor[]) native_query_effects();
}
- //--------------------------------------------------------------------------
+ // --------------------------------------------------------------------------
// Control methods
- //--------------------
-
- /**
- * Enable effect engine.
- * @return {@link #NO_ERROR} in case of success,
- * {@link #INVALID_OPERATION} or {@link #DEAD_OBJECT} in case of failure.
- * @throws IllegalStateException
- */
- public int enable()
- throws IllegalStateException {
- checkState("enable()");
- return native_enable();
- }
+ // --------------------
/**
- * Disable effect engine.
- * @return NO_ERROR in case of success,
- * INVALID_OPERATION or DEAD_OBJECT in case of failure.
+ * Enable or disable effect engine.
+ *
+ * @param enabled the requested enable state
+ * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION}
+ * or {@link #ERROR_DEAD_OBJECT} in case of failure.
* @throws IllegalStateException
*/
- public int disable()
- throws IllegalStateException {
- checkState("disable()");
- return native_disable();
+ public int setEnabled(boolean enabled) throws IllegalStateException {
+ checkState("setEnabled()");
+ return native_setEnabled(enabled);
}
/**
* Set effect parameter. The setParameter method is provided in several
- * forms addressing most common parameter formats. This form is the
- * most generic one where the parameter and its value are both specified
- * as an array of bytes. The parameter and value type and length are therefore
- * totally free. For standard effect defined by OpenSL ES, the parameter format
- * and values must match the definitions in the corresponding OpenSL ES interface.
+ * forms addressing most common parameter formats. This form is the most
+ * generic one where the parameter and its value are both specified as an
+ * array of bytes. The parameter and value type and length are therefore
+ * totally free. For standard effect defined by OpenSL ES, the parameter
+ * format and values must match the definitions in the corresponding OpenSL
+ * ES interface.
*
- * @param param: the identifier of the parameter to set
- * @param value: the new value for the specified parameter
- * @return NO_ERROR in case of success,
- * {@link #BAD_VALUE}, {@link #NO_MEMORY}, {@link #INVALID_OPERATION} or {@link DEAD_OBJECT} in case of failure
+ * @param param the identifier of the parameter to set
+ * @param value the new value for the specified parameter
+ * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE},
+ * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or
+ * {@link #ERROR_DEAD_OBJECT} in case of failure
* @throws IllegalStateException
*/
public int setParameter(byte[] param, byte[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
checkState("setParameter()");
return native_setParameter(param.length, param, value.length, value);
}
/**
* Set effect parameter. The parameter and its value are integers.
- * @see #setParameter(byte[], byte[])
+ *
+ * @see #setParameter(byte[], byte[])
*/
- public int setParameter(int param, int value)
- throws IllegalStateException {
+ public int setParameter(int param, int value) throws IllegalStateException {
byte[] p = intToByteArray(param);
byte[] v = intToByteArray(value);
return setParameter(p, v);
}
/**
- * Set effect parameter. The parameter is an integer and the value is a short integer.
- * @see #setParameter(byte[], byte[])
+ * Set effect parameter. The parameter is an integer and the value is a
+ * short integer.
+ *
+ * @see #setParameter(byte[], byte[])
*/
public int setParameter(int param, short value)
- throws IllegalStateException {
+ throws IllegalStateException {
byte[] p = intToByteArray(param);
byte[] v = shortToByteArray(value);
return setParameter(p, v);
}
/**
- * Set effect parameter. The parameter is an integer and the value is an array of bytes.
- * @see #setParameter(byte[], byte[])
+ * Set effect parameter. The parameter is an integer and the value is an
+ * array of bytes.
+ *
+ * @see #setParameter(byte[], byte[])
*/
public int setParameter(int param, byte[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
byte[] p = intToByteArray(param);
return setParameter(p, value);
}
/**
- * Set effect parameter. The parameter is an array of 1 or 2 integers and the value
- * is also an array of 1 or 2 integers
- * @see #setParameter(byte[], byte[])
+ * Set effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is also an array of 1 or 2 integers
+ *
+ * @see #setParameter(byte[], byte[])
*/
public int setParameter(int[] param, int[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (param.length > 2 || value.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param[0]);
if (param.length > 1) {
@@ -386,14 +460,15 @@ public class AudioEffect
}
/**
- * Set effect parameter. The parameter is an array of 1 or 2 integers and the value
- * is an array of 1 or 2 short integers
- * @see #setParameter(byte[], byte[])
+ * Set effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is an array of 1 or 2 short integers
+ *
+ * @see #setParameter(byte[], byte[])
*/
public int setParameter(int[] param, short[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (param.length > 2 || value.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param[0]);
if (param.length > 1) {
@@ -410,14 +485,15 @@ public class AudioEffect
}
/**
- * Set effect parameter. The parameter is an array of 1 or 2 integers and the value
- * is an array of bytes
- * @see #setParameter(byte[], byte[])
+ * Set effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is an array of bytes
+ *
+ * @see #setParameter(byte[], byte[])
*/
public int setParameter(int[] param, byte[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (param.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param[0]);
if (param.length > 1) {
@@ -429,20 +505,23 @@ public class AudioEffect
/**
* Get effect parameter. The getParameter method is provided in several
- * forms addressing most common parameter formats. This form is the
- * most generic one where the parameter and its value are both specified
- * as an array of bytes. The parameter and value type and length are therefore
+ * forms addressing most common parameter formats. This form is the most
+ * generic one where the parameter and its value are both specified as an
+ * array of bytes. The parameter and value type and length are therefore
* totally free.
- * @param param: the identifier of the parameter to set
- * @param value: the new value for the specified parameter
- * @return NO_ERROR in case of success,
- * {@link #BAD_VALUE}, {@link #NO_MEMORY}, {@link #INVALID_OPERATION} or {@link DEAD_OBJECT} in case of failure
- * When called, value.length indicates the maximum size of the returned parameters value.
- * When returning, value.length is updated with the actual size of the returned value.
+ *
+ * @param param the identifier of the parameter to set
+ * @param value the new value for the specified parameter
+ * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE},
+ * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or
+ * {@link #ERROR_DEAD_OBJECT} in case of failure When called, value.length
+ * indicates the maximum size of the returned parameters value. When
+ * returning, value.length is updated with the actual size of the
+ * returned value.
* @throws IllegalStateException
*/
public int getParameter(byte[] param, byte[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
checkState("getParameter()");
int[] vSize = new int[1];
vSize[0] = value.length;
@@ -456,25 +535,28 @@ public class AudioEffect
}
/**
- * Get effect parameter. The parameter is an integer and the value is an array of bytes.
- * @see #getParameter(byte[], byte[])
+ * Get effect parameter. The parameter is an integer and the value is an
+ * array of bytes.
+ *
+ * @see #getParameter(byte[], byte[])
*/
public int getParameter(int param, byte[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
byte[] p = intToByteArray(param);
return getParameter(p, value);
}
/**
- * Get effect parameter. The parameter is an integer and the value
- * is an array of 1 or 2 integers
- * @see #getParameter(byte[], byte[])
+ * Get effect parameter. The parameter is an integer and the value is an
+ * array of 1 or 2 integers
+ *
+ * @see #getParameter(byte[], byte[])
*/
public int getParameter(int param, int[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (value.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param);
@@ -490,14 +572,15 @@ public class AudioEffect
}
/**
- * Get effect parameter. The parameter is an integer and the value
- * is an array of 1 or 2 short integers
- * @see #getParameter(byte[], byte[])
+ * Get effect parameter. The parameter is an integer and the value is an
+ * array of 1 or 2 short integers
+ *
+ * @see #getParameter(byte[], byte[])
*/
public int getParameter(int param, short[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (value.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param);
@@ -513,14 +596,15 @@ public class AudioEffect
}
/**
- * Get effect parameter. The parameter is an array of 1 or 2 integers and the value
- * is also an array of 1 or 2 integers
- * @see #getParameter(byte[], byte[])
+ * Get effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is also an array of 1 or 2 integers
+ *
+ * @see #getParameter(byte[], byte[])
*/
public int getParameter(int[] param, int[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (param.length > 2 || value.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param[0]);
if (param.length > 1) {
@@ -539,14 +623,15 @@ public class AudioEffect
}
/**
- * Get effect parameter. The parameter is an array of 1 or 2 integers and the value
- * is an array of 1 or 2 short integers
- * @see #getParameter(byte[], byte[])
+ * Get effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is an array of 1 or 2 short integers
+ *
+ * @see #getParameter(byte[], byte[])
*/
public int getParameter(int[] param, short[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (param.length > 2 || value.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param[0]);
if (param.length > 1) {
@@ -565,14 +650,15 @@ public class AudioEffect
}
/**
- * Get effect parameter. The parameter is an array of 1 or 2 integers and the value
- * is an array of bytes
- * @see #getParameter(byte[], byte[])
+ * Get effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is an array of bytes
+ *
+ * @see #getParameter(byte[], byte[])
*/
public int getParameter(int[] param, byte[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (param.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param[0]);
if (param.length > 1) {
@@ -583,19 +669,19 @@ public class AudioEffect
return getParameter(p, value);
}
-
/**
- * Send a command to the effect engine. This method is intended to send proprietary
- * commands to a particular effect implementation.
+ * Send a command to the effect engine. This method is intended to send
+ * proprietary commands to a particular effect implementation.
*
*/
public int command(int cmdCode, byte[] command, byte[] reply)
- throws IllegalStateException {
+ throws IllegalStateException {
checkState("command()");
int[] replySize = new int[1];
replySize[0] = reply.length;
- int status = native_command(cmdCode, command.length, command, replySize, reply);
+ int status = native_command(cmdCode, command.length, command,
+ replySize, reply);
if (reply.length > replySize[0]) {
byte[] resizedReply = new byte[replySize[0]];
@@ -605,51 +691,53 @@ public class AudioEffect
return status;
}
- //--------------------------------------------------------------------------
+ // --------------------------------------------------------------------------
// Getters
- //--------------------
+ // --------------------
/**
- * Returns effect unique identifier. This system wide unique identifier
- * can be used to attach this effect to a MediaPlayer or an AudioTrack
- * when the effect is an auxiliary effect (Reverb)
+ * Returns effect unique identifier. This system wide unique identifier can
+ * be used to attach this effect to a MediaPlayer or an AudioTrack when the
+ * effect is an auxiliary effect (Reverb)
+ *
* @return the effect identifier.
* @throws IllegalStateException
*/
- public int getId()
- throws IllegalStateException {
+ public int getId() throws IllegalStateException {
checkState("getId()");
return mId;
}
/**
* Returns effect engine enable state
+ *
* @return true if the effect is enabled, false otherwise.
* @throws IllegalStateException
*/
- public boolean getEnable()
- throws IllegalStateException {
- checkState("getEnable()");
- return native_getEnable();
+ public boolean getEnabled() throws IllegalStateException {
+ checkState("getEnabled()");
+ return native_getEnabled();
}
/**
* Checks if this AudioEffect object is controlling the effect engine.
- * @return true if this instance has control of effect engine, false otherwise.
+ *
+ * @return true if this instance has control of effect engine, false
+ * otherwise.
* @throws IllegalStateException
*/
- public boolean hasControl()
- throws IllegalStateException {
+ public boolean hasControl() throws IllegalStateException {
checkState("hasControl()");
return native_hasControl();
}
- //--------------------------------------------------------------------------
+ // --------------------------------------------------------------------------
// Initialization / configuration
- //--------------------
+ // --------------------
/**
* Sets the listener AudioEffect notifies when the effect engine is enabled
* or disabled.
+ *
* @param listener
*/
public void setEnableStatusListener(OnEnableStatusChangeListener listener) {
@@ -662,8 +750,9 @@ public class AudioEffect
}
/**
- * Sets the listener AudioEffect notifies when the effect engine control
- * is taken or returned.
+ * Sets the listener AudioEffect notifies when the effect engine control is
+ * taken or returned.
+ *
* @param listener
*/
public void setControlStatusListener(OnControlStatusChangeListener listener) {
@@ -677,6 +766,7 @@ public class AudioEffect
/**
* Sets the listener AudioEffect notifies when a parameter is changed.
+ *
* @param listener
*/
public void setParameterListener(OnParameterChangeListener listener) {
@@ -691,7 +781,7 @@ public class AudioEffect
// Convenience method for the creation of the native event handler
// It is called only when a non-null event listener is set.
// precondition:
- // mNativeEventHandler is null
+ // mNativeEventHandler is null
private void createNativeEventHandler() {
Looper looper;
if ((looper = Looper.myLooper()) != null) {
@@ -703,52 +793,62 @@ public class AudioEffect
}
}
- //---------------------------------------------------------
+ // ---------------------------------------------------------
// Interface definitions
- //--------------------
+ // --------------------
/**
- * Interface definition for a callback to be invoked when the
- * effect engine is enabled or disabled.
+ * The OnParameterChangeListener interface defines a method called by the AudioEffect
+ * when a the enabled state of the effect engine was changed by the controlling application.
*/
- public interface OnEnableStatusChangeListener {
+ public interface OnEnableStatusChangeListener {
/**
- * Called on the listener to notify it that the effect engine
- * has been enabled or disabled.
+ * Called on the listener to notify it that the effect engine has been
+ * enabled or disabled.
+ * @param effect the effect on which the interface is registered.
+ * @param enabled new effect state.
*/
void onEnableStatusChange(AudioEffect effect, boolean enabled);
}
/**
- * Interface definition for a callback to be invoked when the
- * effect engine control is taken or returned.
+ * The OnControlStatusChangeListener interface defines a method called by the AudioEffect
+ * when a the control of the effect engine is gained or lost by the application
*/
- public interface OnControlStatusChangeListener {
+ public interface OnControlStatusChangeListener {
/**
- * Called on the listener to notify it that the effect engine
- * control has been taken or returned.
+ * Called on the listener to notify it that the effect engine control
+ * has been taken or returned.
+ * @param effect the effect on which the interface is registered.
+ * @param controlGranted true if the application has been granted control of the effect
+ * engine, false otherwise.
*/
void onControlStatusChange(AudioEffect effect, boolean controlGranted);
}
/**
- * Interface definition for a callback to be invoked when a
- * parameter value has changed.
+ * The OnParameterChangeListener interface defines a method called by the AudioEffect
+ * when a parameter is changed in the effect engine by the controlling application.
*/
- public interface OnParameterChangeListener {
+ public interface OnParameterChangeListener {
/**
* Called on the listener to notify it that a parameter value has changed.
+ * @param effect the effect on which the interface is registered.
+ * @param status status of the set parameter operation.
+ * @param param ID of the modified parameter.
+ * @param value the new parameter value.
*/
- void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value);
+ void onParameterChange(AudioEffect effect, int status, byte[] param,
+ byte[] value);
}
- //---------------------------------------------------------
+ // ---------------------------------------------------------
// Inner classes
- //--------------------
+ // --------------------
/**
- * Helper class to handle the forwarding of native events to the appropriate listeners
+ * Helper class to handle the forwarding of native events to the appropriate
+ * listeners
*/
- private class NativeEventHandler extends Handler
- {
+ private class NativeEventHandler extends Handler {
private AudioEffect mAudioEffect;
public NativeEventHandler(AudioEffect ae, Looper looper) {
@@ -761,14 +861,15 @@ public class AudioEffect
if (mAudioEffect == null) {
return;
}
- switch(msg.what) {
+ switch (msg.what) {
case NATIVE_EVENT_ENABLED_STATUS:
OnEnableStatusChangeListener enableStatusChangeListener = null;
synchronized (mListenerLock) {
enableStatusChangeListener = mAudioEffect.mEnableStatusChangeListener;
}
if (enableStatusChangeListener != null) {
- enableStatusChangeListener.onEnableStatusChange(mAudioEffect, (boolean)(msg.arg1 != 0));
+ enableStatusChangeListener.onEnableStatusChange(
+ mAudioEffect, (boolean) (msg.arg1 != 0));
}
break;
case NATIVE_EVENT_CONTROL_STATUS:
@@ -777,7 +878,8 @@ public class AudioEffect
controlStatusChangeListener = mAudioEffect.mControlChangeStatusListener;
}
if (controlStatusChangeListener != null) {
- controlStatusChangeListener.onControlStatusChange(mAudioEffect, (boolean)(msg.arg1 != 0));
+ controlStatusChangeListener.onControlStatusChange(
+ mAudioEffect, (boolean) (msg.arg1 != 0));
}
break;
case NATIVE_EVENT_PARAMETER_CHANGED:
@@ -786,10 +888,12 @@ public class AudioEffect
parameterChangeListener = mAudioEffect.mParameterChangeListener;
}
if (parameterChangeListener != null) {
- // arg1 contains offset of parameter value from start of byte array
+ // arg1 contains offset of parameter value from start of
+ // byte array
int vOffset = msg.arg1;
- byte[] p = (byte[])msg.obj;
- // See effect_param_t in EffectApi.h for psize and vsize fields offsets
+ byte[] p = (byte[]) msg.obj;
+ // See effect_param_t in EffectApi.h for psize and vsize
+ // fields offsets
int status = byteArrayToInt(p, 0);
int psize = byteArrayToInt(p, 4);
int vsize = byteArrayToInt(p, 8);
@@ -798,90 +902,76 @@ public class AudioEffect
System.arraycopy(p, 12, param, 0, psize);
System.arraycopy(p, vOffset, value, 0, vsize);
- parameterChangeListener.onParameterChange(mAudioEffect, status, param, value);
+ parameterChangeListener.onParameterChange(mAudioEffect,
+ status, param, value);
}
break;
- default:
+ default:
Log.e(TAG, "handleMessage() Unknown event type: " + msg.what);
break;
}
}
}
-
- //---------------------------------------------------------
+ // ---------------------------------------------------------
// Java methods called from the native side
- //--------------------
+ // --------------------
@SuppressWarnings("unused")
- private static void postEventFromNative(Object effect_ref,
- int what, int arg1, int arg2, Object obj) {
- AudioEffect effect = (AudioEffect)((WeakReference)effect_ref).get();
+ private static void postEventFromNative(Object effect_ref, int what,
+ int arg1, int arg2, Object obj) {
+ AudioEffect effect = (AudioEffect) ((WeakReference) effect_ref).get();
if (effect == null) {
return;
}
if (effect.mNativeEventHandler != null) {
- Message m = effect.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj);
+ Message m = effect.mNativeEventHandler.obtainMessage(what, arg1,
+ arg2, obj);
effect.mNativeEventHandler.sendMessage(m);
}
}
-
- //---------------------------------------------------------
+ // ---------------------------------------------------------
// Native methods called from the Java side
- //--------------------
+ // --------------------
private static native final void native_init();
- private native final int native_setup(Object audioeffect_this,
- String type,
- String uuid,
- int priority,
- int audioSession,
- int[] id,
- Object[] desc);
+ private native final int native_setup(Object audioeffect_this, String type,
+ String uuid, int priority, int audioSession, int[] id, Object[] desc);
private native final void native_finalize();
private native final void native_release();
- private native final int native_enable();
+ private native final int native_setEnabled(boolean enabled);
- private native final int native_disable();
-
- private native final boolean native_getEnable();
+ private native final boolean native_getEnabled();
private native final boolean native_hasControl();
- private native final int native_setParameter(int psize,
- byte[] param,
- int vsize,
- byte[] value);
+ private native final int native_setParameter(int psize, byte[] param,
+ int vsize, byte[] value);
- private native final int native_getParameter(int psize,
- byte[] param,
- int[] vsize,
- byte[] value);
+ private native final int native_getParameter(int psize, byte[] param,
+ int[] vsize, byte[] value);
- private native final int native_command(int cmdCode,
- int cmdSize,
- byte[] cmdData,
- int[] repSize,
- byte[] repData);
+ private native final int native_command(int cmdCode, int cmdSize,
+ byte[] cmdData, int[] repSize, byte[] repData);
private static native Object[] native_query_effects();
- //---------------------------------------------------------
+ // ---------------------------------------------------------
// Utility methods
- //------------------
+ // ------------------
- protected void checkState(String methodName)
- throws IllegalStateException {
+ protected void checkState(String methodName) throws IllegalStateException {
synchronized (mStateLock) {
if (mState != STATE_INITIALIZED) {
- throw(new IllegalStateException(methodName+" called on uninitialized AudioEffect."));
+ throw (new IllegalStateException(methodName
+ + " called on uninitialized AudioEffect."));
}
}
}
@@ -890,10 +980,12 @@ public class AudioEffect
switch (status) {
case AudioEffect.SUCCESS:
break;
- case AudioEffect.BAD_VALUE:
- throw (new IllegalArgumentException("AudioEffect: bad parameter value"));
- case AudioEffect.INVALID_OPERATION:
- throw (new UnsupportedOperationException("AudioEffect: invalid parameter operation"));
+ case AudioEffect.ERROR_BAD_VALUE:
+ throw (new IllegalArgumentException(
+ "AudioEffect: bad parameter value"));
+ case AudioEffect.ERROR_INVALID_OPERATION:
+ throw (new UnsupportedOperationException(
+ "AudioEffect: invalid parameter operation"));
default:
throw (new RuntimeException("AudioEffect: set/get parameter error"));
}
@@ -903,6 +995,7 @@ public class AudioEffect
return byteArrayToInt(valueBuf, 0);
}
+
protected int byteArrayToInt(byte[] valueBuf, int offset) {
ByteBuffer converter = ByteBuffer.wrap(valueBuf);
converter.order(ByteOrder.nativeOrder());
@@ -931,12 +1024,12 @@ public class AudioEffect
protected byte[] shortToByteArray(short value) {
ByteBuffer converter = ByteBuffer.allocate(2);
converter.order(ByteOrder.nativeOrder());
- short sValue = (short)value;
+ short sValue = (short) value;
converter.putShort(sValue);
return converter.array();
}
- protected byte[] concatArrays(byte[] ...arrays) {
+ protected byte[] concatArrays(byte[]... arrays) {
int len = 0;
for (byte[] a : arrays) {
len += a.length;
diff --git a/media/java/android/media/Visualizer.java b/media/java/android/media/Visualizer.java
new file mode 100755
index 0000000..cdd3cdf
--- /dev/null
+++ b/media/java/android/media/Visualizer.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.media;
+
+import android.util.Log;
+import java.lang.ref.WeakReference;
+import java.io.IOException;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * The Visualizer class enables application to retrieve part of the currently playing audio for
+ * visualization purpose. It is not an audio recording interface and only returns partial and low
+ * quality audio content. However, to protect privacy of certain audio data (e.g voice mail) the use
+ * of the visualizer requires the permission android.permission.RECORD_AUDIO.
+ * <p>The audio session ID passed to the constructor indicates which audio content should be
+ * visualized:<br>
+ * <ul>
+ * <li>If the session is 0, the audio output mix is visualized</li>
+ * <li>If the session is not 0, the audio from a particular {@link MediaPlayer} or
+ * {@link AudioTrack}
+ * using this audio session is visualized </li>
+ * </ul>
+ * <p>Two types of representation of audio content can be captured: <br>
+ * <ul>
+ * <li>Waveform data: consecutive 8-bit (unsigned) mono samples by using the
+ * {@link #getWaveForm(byte[])} method</li>
+ * <li>Frequency data: 8-bit magnitude FFT by using the {@link #getFft(byte[])} method</li>
+ * </ul>
+ * <p>The length of the capture can be retrieved or specified by calling respectively
+ * {@link #getCaptureSize()} and {@link #setCaptureSize(int)} methods. Note that the size of the FFT
+ * is half of the specified capture size but both sides of the spectrum are returned yielding in a
+ * number of bytes equal to the capture size. The capture size must be a power of 2 in the range
+ * returned by {@link #getCaptureSizeRange()}.
+ * <p>In addition to the polling capture mode described above with {@link #getWaveForm(byte[])} and
+ * {@link #getFft(byte[])} methods, a callback mode is also available by installing a listener by
+ * use of the {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
+ * The rate at which the listener capture method is called as well as the type of data returned is
+ * specified.
+ * <p>Before capturing data, the Visualizer must be enabled by calling the
+ * {@link #setEnabled(boolean)} method.
+ * When data capture is not needed any more, the Visualizer should be disabled.
+ * <p>It is good practice to call the {@link #release()} method when the Visualizer is not used
+ * anymore to free up native resources associated to the Visualizer instance.
+ *
+ * {@hide Pending API council review}
+ */
+
+public class Visualizer {
+
+ static {
+ System.loadLibrary("audioeffect_jni");
+ native_init();
+ }
+
+ private final static String TAG = "Visualizer-JAVA";
+
+ /**
+ * State of a Visualizer object that was not successfully initialized upon creation
+ */
+ public static final int STATE_UNINITIALIZED = 0;
+ /**
+ * State of a Visualizer object that is ready to be used.
+ */
+ public static final int STATE_INITIALIZED = 1;
+ /**
+ * State of a Visualizer object that is active.
+ */
+ public static final int STATE_ENABLED = 2;
+
+ // to keep in sync with frameworks/base/media/jni/audioeffect/android_media_Visualizer.cpp
+ protected static final int NATIVE_EVENT_PCM_CAPTURE = 0;
+ protected static final int NATIVE_EVENT_FFT_CAPTURE = 1;
+
+ // Error codes:
+ /**
+ * Successful operation.
+ */
+ public static final int SUCCESS = 0;
+ /**
+ * Unspecified error.
+ */
+ public static final int ERROR = -1;
+ /**
+ * Internal opreation status. Not returned by any method.
+ */
+ public static final int ALREADY_EXISTS = -2;
+ /**
+ * Operation failed due to bad object initialization.
+ */
+ public static final int ERROR_NO_INIT = -3;
+ /**
+ * Operation failed due to bad parameter value.
+ */
+ public static final int ERROR_BAD_VALUE = -4;
+ /**
+ * Operation failed because it was requested in wrong state.
+ */
+ public static final int ERROR_INVALID_OPERATION = -5;
+ /**
+ * Operation failed due to lack of memory.
+ */
+ public static final int ERROR_NO_MEMORY = -6;
+ /**
+ * Operation failed due to dead remote object.
+ */
+ public static final int ERROR_DEAD_OBJECT = -7;
+
+ //--------------------------------------------------------------------------
+ // Member variables
+ //--------------------
+ /**
+ * Indicates the state of the Visualizer instance
+ */
+ protected int mState = STATE_UNINITIALIZED;
+ /**
+ * Lock to synchronize access to mState
+ */
+ protected final Object mStateLock = new Object();
+ /**
+ * System wide unique Identifier of the visualizer engine used by this Visualizer instance
+ */
+ protected int mId;
+
+ /**
+ * Lock to protect listeners updates against event notifications
+ */
+ protected final Object mListenerLock = new Object();
+ /**
+ * Handler for events coming from the native code
+ */
+ protected NativeEventHandler mNativeEventHandler = null;
+ /**
+ * PCM and FFT capture listener registered by client
+ */
+ protected OnDataCaptureListener mCaptureListener = null;
+
+ // accessed by native methods
+ private int mNativeVisualizer;
+ private int mJniData;
+
+ //--------------------------------------------------------------------------
+ // Constructor, Finalize
+ //--------------------
+ /**
+ * Class constructor.
+ * @param audioSession System wide unique audio session identifier. If audioSession
+ * is not 0, the visualizer will be attached to the MediaPlayer or AudioTrack in the
+ * same audio session. Otherwise, the Visualizer will apply to the output mix.
+ *
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+
+ public Visualizer(int audioSession)
+ throws UnsupportedOperationException, RuntimeException {
+ int[] id = new int[1];
+
+ synchronized (mStateLock) {
+ mState = STATE_UNINITIALIZED;
+ // native initialization
+ int result = native_setup(new WeakReference<Visualizer>(this), audioSession, id);
+ if (result != SUCCESS && result != ALREADY_EXISTS) {
+ Log.e(TAG, "Error code "+result+" when initializing Visualizer.");
+ switch (result) {
+ case ERROR_INVALID_OPERATION:
+ throw (new UnsupportedOperationException("Effect library not loaded"));
+ default:
+ throw (new RuntimeException("Cannot initialize Visualizer engine, error: "
+ +result));
+ }
+ }
+ mId = id[0];
+ if (native_getEnabled()) {
+ mState = STATE_ENABLED;
+ } else {
+ mState = STATE_INITIALIZED;
+ }
+ }
+ }
+
+ /**
+ * Releases the native Visualizer resources. It is a good practice to release the
+ * visualization engine when not in use.
+ */
+ public void release() {
+ synchronized (mStateLock) {
+ native_release();
+ mState = STATE_UNINITIALIZED;
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ native_finalize();
+ }
+
+ /**
+ * Enable or disable the visualization engine.
+ * @param enabled requested enable state
+ * @return {@link #SUCCESS} in case of success,
+ * {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} in case of failure.
+ * @throws IllegalStateException
+ */
+ public int setEnabled(boolean enabled)
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if ((enabled && mState != STATE_INITIALIZED) ||
+ (!enabled && mState != STATE_ENABLED)) {
+ throw(new IllegalStateException("setEnabled() called in wrong state: "+mState));
+ }
+ int status = native_setEnabled(enabled);
+ if (status == SUCCESS) {
+ mState = enabled ? STATE_ENABLED : STATE_INITIALIZED;
+ }
+ return status;
+ }
+ }
+
+ /**
+ * Get current activation state of the visualizer.
+ * @return true if the visualizer is active, false otherwise
+ */
+ public boolean getEnabled()
+ {
+ synchronized (mStateLock) {
+ if (mState == STATE_UNINITIALIZED) {
+ throw(new IllegalStateException("getEnabled() called in wrong state: "+mState));
+ }
+ return native_getEnabled();
+ }
+ }
+
+ /**
+ * Returns the capture size range.
+ * @return the mininum capture size is returned in first array element and the maximum in second
+ * array element.
+ */
+ public static native int[] getCaptureSizeRange();
+
+ /**
+ * Returns the maximum capture rate for the callback capture method. This is the maximum value
+ * for the rate parameter of the
+ * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
+ * @return the maximum capture rate expressed in milliHertz
+ */
+ public static native int getMaxCaptureRate();
+
+ /**
+ * Sets the capture size, i.e. the number of bytes returned by {@link #getWaveForm(byte[])} and
+ * {@link #getFft(byte[])} methods. The capture size must be a power of 2 in the range returned
+ * by {@link #getCaptureSizeRange()}.
+ * This method must not be called when the Visualizer is enabled.
+ * @param size requested capture size
+ * @return {@link #SUCCESS} in case of success,
+ * {@link #ERROR_BAD_VALUE} in case of failure.
+ * @throws IllegalStateException
+ */
+ public int setCaptureSize(int size)
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState != STATE_INITIALIZED) {
+ throw(new IllegalStateException("setCaptureSize() called in wrong state: "+mState));
+ }
+ return native_setCaptureSize(size);
+ }
+ }
+
+ /**
+ * Returns current capture size.
+ * @return the capture size in bytes.
+ */
+ public int getCaptureSize()
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState == STATE_UNINITIALIZED) {
+ throw(new IllegalStateException("getCaptureSize() called in wrong state: "+mState));
+ }
+ return native_getCaptureSize();
+ }
+ }
+
+ /**
+ * Returns the sampling rate of the captured audio.
+ * @return the sampling rate in milliHertz.
+ */
+ public int getSamplingRate()
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState == STATE_UNINITIALIZED) {
+ throw(new IllegalStateException("getSamplingRate() called in wrong state: "+mState));
+ }
+ return native_getSamplingRate();
+ }
+ }
+
+ /**
+ * Returns a waveform capture of currently playing audio content. The capture consists in
+ * a number of consecutive 8-bit (unsigned) mono PCM samples equal to the capture size returned
+ * by {@link #getCaptureSize()}.
+ * <p>This method must be called when the Visualizer is enabled.
+ * @param waveform array of bytes where the waveform should be returned
+ * @return {@link #SUCCESS} in case of success,
+ * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
+ * in case of failure.
+ * @throws IllegalStateException
+ */
+ public int getWaveForm(byte[] waveform)
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState != STATE_ENABLED) {
+ throw(new IllegalStateException("getWaveForm() called in wrong state: "+mState));
+ }
+ return native_getWaveForm(waveform);
+ }
+ }
+ /**
+ * Returns a frequency capture of currently playing audio content. The capture is a 8-bit
+ * magnitude FFT. Note that the size of the FFT is half of the specified capture size but both
+ * sides of the spectrum are returned yielding in a number of bytes equal to the capture size.
+ * {@see #getCaptureSize()}.
+ * <p>This method must be called when the Visualizer is enabled.
+ * @param fft array of bytes where the FFT should be returned
+ * @return {@link #SUCCESS} in case of success,
+ * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
+ * in case of failure.
+ * @throws IllegalStateException
+ */
+ public int getFft(byte[] fft)
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState != STATE_ENABLED) {
+ throw(new IllegalStateException("getFft() called in wrong state: "+mState));
+ }
+ return native_getFft(fft);
+ }
+ }
+
+ //---------------------------------------------------------
+ // Interface definitions
+ //--------------------
+ /**
+ * The OnDataCaptureListener interface defines methods called by the Visualizer to periodically
+ * update the audio visualization capture.
+ * The client application can implement this interface and register the listener with the
+ * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
+ */
+ public interface OnDataCaptureListener {
+ /**
+ * Method called when a new waveform capture is available.
+ * @param visualizer Visualizer object on which the listener is registered.
+ * @param waveform array of bytes containing the waveform representation.
+ * @param samplingRate sampling rate of the audio visualized.
+ */
+ void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate);
+
+ /**
+ * Method called when a new frequency capture is available.
+ * @param visualizer Visualizer object on which the listener is registered.
+ * @param fft array of bytes containing the frequency representation.
+ * @param samplingRate sampling rate of the audio visualized.
+ */
+ void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate);
+ }
+
+ /**
+ * Registers an OnDataCaptureListener interface and specifies the rate at which the capture
+ * should be updated as well as the type of capture requested.
+ * <p>Call this method with a null listener to stop receiving the capture updates.
+ * @param listener OnDataCaptureListener registered
+ * @param rate rate in milliHertz at which the capture should be updated
+ * @param waveform true if a waveform capture is requested: the onWaveFormDataCapture()
+ * method will be called on the OnDataCaptureListener interface.
+ * @param fft true if a frequency capture is requested: the onFftDataCapture() method will be
+ * called on the OnDataCaptureListener interface.
+ * @return {@link #SUCCESS} in case of success,
+ * {@link #ERROR_NO_INIT} or {@link #ERROR_BAD_VALUE} in case of failure.
+ */
+ public int setDataCaptureListener(OnDataCaptureListener listener,
+ int rate, boolean waveform, boolean fft) {
+ synchronized (mListenerLock) {
+ mCaptureListener = listener;
+ }
+ if (listener == null) {
+ // make sure capture callback is stopped in native code
+ waveform = false;
+ fft = false;
+ }
+ int status = native_setPeriodicCapture(rate, waveform, fft);
+ if (status == SUCCESS) {
+ if ((listener != null) && (mNativeEventHandler == null)) {
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mNativeEventHandler = new NativeEventHandler(this, looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ mNativeEventHandler = new NativeEventHandler(this, looper);
+ } else {
+ mNativeEventHandler = null;
+ status = ERROR_NO_INIT;
+ }
+ }
+ }
+ return status;
+ }
+
+ /**
+ * Helper class to handle the forwarding of native events to the appropriate listeners
+ */
+ private class NativeEventHandler extends Handler
+ {
+ private Visualizer mVisualizer;
+
+ public NativeEventHandler(Visualizer v, Looper looper) {
+ super(looper);
+ mVisualizer = v;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (mVisualizer == null) {
+ return;
+ }
+ OnDataCaptureListener l = null;
+ synchronized (mListenerLock) {
+ l = mVisualizer.mCaptureListener;
+ }
+
+ if (l != null) {
+ byte[] data = (byte[])msg.obj;
+ int samplingRate = msg.arg1;
+ switch(msg.what) {
+ case NATIVE_EVENT_PCM_CAPTURE:
+ l.onWaveFormDataCapture(mVisualizer, data, samplingRate);
+ break;
+ case NATIVE_EVENT_FFT_CAPTURE:
+ l.onFftDataCapture(mVisualizer, data, samplingRate);
+ break;
+ default:
+ Log.e(TAG,"Unknown native event: "+msg.what);
+ break;
+ }
+ }
+ }
+ }
+
+ //---------------------------------------------------------
+ // Interface definitions
+ //--------------------
+
+ private static native final void native_init();
+
+ private native final int native_setup(Object audioeffect_this,
+ int audioSession,
+ int[] id);
+
+ private native final void native_finalize();
+
+ private native final void native_release();
+
+ private native final int native_setEnabled(boolean enabled);
+
+ private native final boolean native_getEnabled();
+
+ private native final int native_setCaptureSize(int size);
+
+ private native final int native_getCaptureSize();
+
+ private native final int native_getSamplingRate();
+
+ private native final int native_getWaveForm(byte[] waveform);
+
+ private native final int native_getFft(byte[] fft);
+
+ private native final int native_setPeriodicCapture(int rate, boolean waveForm, boolean fft);
+
+ //---------------------------------------------------------
+ // Java methods called from the native side
+ //--------------------
+ @SuppressWarnings("unused")
+ private static void postEventFromNative(Object effect_ref,
+ int what, int arg1, int arg2, Object obj) {
+ Visualizer visu = (Visualizer)((WeakReference)effect_ref).get();
+ if (visu == null) {
+ return;
+ }
+
+ if (visu.mNativeEventHandler != null) {
+ Message m = visu.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj);
+ visu.mNativeEventHandler.sendMessage(m);
+ }
+
+ }
+
+}
+
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index c5250d7..aedb54a 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -681,18 +681,6 @@ android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz)
}
static jint
-android_media_MediaPlayer_snoop(JNIEnv* env, jobject thiz, jobject data, jint kind) {
- jshort* ar = (jshort*)env->GetPrimitiveArrayCritical((jarray)data, 0);
- jsize len = env->GetArrayLength((jarray)data);
- int ret = 0;
- if (ar) {
- ret = MediaPlayer::snoop(ar, len, kind);
- env->ReleasePrimitiveArrayCritical((jarray)data, ar, 0);
- }
- return ret;
-}
-
-static jint
android_media_MediaPlayer_native_suspend_resume(
JNIEnv *env, jobject thiz, jboolean isSuspend) {
LOGV("suspend_resume(%d)", isSuspend);
@@ -757,7 +745,6 @@ static JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_MediaPlayer_native_init},
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
- {"snoop", "([SI)I", (void *)android_media_MediaPlayer_snoop},
{"native_suspend_resume", "(Z)I", (void *)android_media_MediaPlayer_native_suspend_resume},
{"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id},
{"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
diff --git a/media/jni/audioeffect/Android.mk b/media/jni/audioeffect/Android.mk
index d03b63b..4c5cf71 100644
--- a/media/jni/audioeffect/Android.mk
+++ b/media/jni/audioeffect/Android.mk
@@ -2,7 +2,8 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- android_media_AudioEffect.cpp
+ android_media_AudioEffect.cpp \
+ android_media_Visualizer.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 17f2d8f..02474a4 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -455,9 +455,8 @@ static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz)
env->SetIntField(thiz, fields.fidJniData, 0);
}
-
static jint
-android_media_AudioEffect_native_enable(JNIEnv *env, jobject thiz)
+android_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
{
// retrieve the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
@@ -469,29 +468,11 @@ android_media_AudioEffect_native_enable(JNIEnv *env, jobject thiz)
return AUDIOEFFECT_ERROR_NO_INIT;
}
- return translateError(lpAudioEffect->enable());
+ return translateError(lpAudioEffect->setEnabled(enabled));
}
-
-static jint
-android_media_AudioEffect_native_disable(JNIEnv *env, jobject thiz)
-{
- // retrieve the AudioEffect object
- AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
- thiz, fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve AudioEffect pointer for disable()");
- return AUDIOEFFECT_ERROR_NO_INIT;
- }
-
- return translateError(lpAudioEffect->disable());
-}
-
-
static jboolean
-android_media_AudioEffect_native_getEnable(JNIEnv *env, jobject thiz)
+android_media_AudioEffect_native_getEnabled(JNIEnv *env, jobject thiz)
{
// retrieve the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
@@ -503,7 +484,7 @@ android_media_AudioEffect_native_getEnable(JNIEnv *env, jobject thiz)
return false;
}
- return (jboolean)lpAudioEffect->isEnabled();
+ return (jboolean)lpAudioEffect->getEnabled();
}
@@ -516,7 +497,7 @@ android_media_AudioEffect_native_hasControl(JNIEnv *env, jobject thiz)
if (lpAudioEffect == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve AudioEffect pointer for getEnabled()");
+ "Unable to retrieve AudioEffect pointer for hasControl()");
return false;
}
@@ -817,9 +798,8 @@ static JNINativeMethod gMethods[] = {
(void *)android_media_AudioEffect_native_setup},
{"native_finalize", "()V", (void *)android_media_AudioEffect_native_finalize},
{"native_release", "()V", (void *)android_media_AudioEffect_native_release},
- {"native_enable", "()I", (void *)android_media_AudioEffect_native_enable},
- {"native_disable", "()I", (void *)android_media_AudioEffect_native_disable},
- {"native_getEnable", "()Z", (void *)android_media_AudioEffect_native_getEnable},
+ {"native_setEnabled", "(Z)I", (void *)android_media_AudioEffect_native_setEnabled},
+ {"native_getEnabled", "()Z", (void *)android_media_AudioEffect_native_getEnabled},
{"native_hasControl", "()Z", (void *)android_media_AudioEffect_native_hasControl},
{"native_setParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_setParameter},
{"native_getParameter", "(I[B[I[B)I", (void *)android_media_AudioEffect_native_getParameter},
@@ -830,6 +810,8 @@ static JNINativeMethod gMethods[] = {
// ----------------------------------------------------------------------------
+extern int register_android_media_visualizer(JNIEnv *env);
+
int register_android_media_AudioEffect(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
@@ -852,6 +834,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
goto bail;
}
+ if (register_android_media_visualizer(env) < 0) {
+ LOGE("ERROR: Visualizer native registration failed\n");
+ goto bail;
+ }
+
/* success -- return valid version number */
result = JNI_VERSION_1_4;
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
new file mode 100644
index 0000000..31119f8
--- /dev/null
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <stdio.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "visualizers-JNI"
+
+#include <utils/Log.h>
+#include <nativehelper/jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include "media/Visualizer.h"
+
+using namespace android;
+
+#define VISUALIZER_SUCCESS 0
+#define VISUALIZER_ERROR -1
+#define VISUALIZER_ERROR_ALREADY_EXISTS -2
+#define VISUALIZER_ERROR_NO_INIT -3
+#define VISUALIZER_ERROR_BAD_VALUE -4
+#define VISUALIZER_ERROR_INVALID_OPERATION -5
+#define VISUALIZER_ERROR_NO_MEMORY -6
+#define VISUALIZER_ERROR_DEAD_OBJECT -7
+
+#define NATIVE_EVENT_PCM_CAPTURE 0
+#define NATIVE_EVENT_FFT_CAPTURE 1
+
+// ----------------------------------------------------------------------------
+static const char* const kClassPathName = "android/media/Visualizer";
+
+struct fields_t {
+ // these fields provide access from C++ to the...
+ jclass clazzEffect; // Visualizer class
+ jmethodID midPostNativeEvent; // event post callback method
+ jfieldID fidNativeVisualizer; // stores in Java the native Visualizer object
+ jfieldID fidJniData; // stores in Java additional resources used by the native Visualizer
+};
+static fields_t fields;
+
+struct visualizer_callback_cookie {
+ jclass visualizer_class; // Visualizer class
+ jobject visualizer_ref; // Visualizer object instance
+ };
+
+// ----------------------------------------------------------------------------
+class visualizerJniStorage {
+ public:
+ visualizer_callback_cookie mCallbackData;
+
+ visualizerJniStorage() {
+ }
+
+ ~visualizerJniStorage() {
+ }
+
+};
+
+
+static jint translateError(int code) {
+ switch(code) {
+ case NO_ERROR:
+ return VISUALIZER_SUCCESS;
+ case ALREADY_EXISTS:
+ return VISUALIZER_ERROR_ALREADY_EXISTS;
+ case NO_INIT:
+ return VISUALIZER_ERROR_NO_INIT;
+ case BAD_VALUE:
+ return VISUALIZER_ERROR_BAD_VALUE;
+ case INVALID_OPERATION:
+ return VISUALIZER_ERROR_INVALID_OPERATION;
+ case NO_MEMORY:
+ return VISUALIZER_ERROR_NO_MEMORY;
+ case DEAD_OBJECT:
+ return VISUALIZER_ERROR_DEAD_OBJECT;
+ default:
+ return VISUALIZER_ERROR;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static void captureCallback(void* user,
+ uint32_t waveformSize,
+ uint8_t *waveform,
+ uint32_t fftSize,
+ uint8_t *fft,
+ uint32_t samplingrate) {
+
+ int arg1 = 0;
+ int arg2 = 0;
+ size_t size;
+
+ visualizer_callback_cookie *callbackInfo = (visualizer_callback_cookie *)user;
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ LOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
+ callbackInfo,
+ callbackInfo->visualizer_ref,
+ callbackInfo->visualizer_class);
+
+ if (!user || !env) {
+ LOGW("captureCallback error user %p, env %p", user, env);
+ return;
+ }
+
+ if (waveformSize != 0 && waveform != NULL) {
+ jbyteArray jArray = env->NewByteArray(waveformSize);
+ if (jArray != NULL) {
+ jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
+ memcpy(nArray, waveform, waveformSize);
+ env->ReleaseByteArrayElements(jArray, nArray, 0);
+ env->CallStaticVoidMethod(
+ callbackInfo->visualizer_class,
+ fields.midPostNativeEvent,
+ callbackInfo->visualizer_ref,
+ NATIVE_EVENT_PCM_CAPTURE,
+ samplingrate,
+ 0,
+ jArray);
+ }
+ }
+
+ if (fftSize != 0 && fft != NULL) {
+ jbyteArray jArray = env->NewByteArray(fftSize);
+ if (jArray != NULL) {
+ jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
+ memcpy(nArray, fft, fftSize);
+ env->ReleaseByteArrayElements(jArray, nArray, 0);
+ env->CallStaticVoidMethod(
+ callbackInfo->visualizer_class,
+ fields.midPostNativeEvent,
+ callbackInfo->visualizer_ref,
+ NATIVE_EVENT_FFT_CAPTURE,
+ samplingrate,
+ 0,
+ jArray);
+ env->DeleteLocalRef(jArray);
+ }
+ }
+
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+}
+
+static Visualizer *getVisualizer(JNIEnv* env, jobject thiz)
+{
+ Visualizer *v = (Visualizer *)env->GetIntField(
+ thiz, fields.fidNativeVisualizer);
+ if (v == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve Visualizer pointer");
+ }
+ return v;
+}
+
+// ----------------------------------------------------------------------------
+// This function gets some field IDs, which in turn causes class initialization.
+// It is called from a static block in Visualizer, which won't run until the
+// first time an instance of this class is used.
+static void
+android_media_visualizer_native_init(JNIEnv *env)
+{
+
+ LOGV("android_media_visualizer_native_init");
+
+ fields.clazzEffect = NULL;
+
+ // Get the Visualizer class
+ jclass clazz = env->FindClass(kClassPathName);
+ if (clazz == NULL) {
+ LOGE("Can't find %s", kClassPathName);
+ return;
+ }
+
+ fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);
+
+ // Get the postEvent method
+ fields.midPostNativeEvent = env->GetStaticMethodID(
+ fields.clazzEffect,
+ "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+ if (fields.midPostNativeEvent == NULL) {
+ LOGE("Can't find Visualizer.%s", "postEventFromNative");
+ return;
+ }
+
+ // Get the variables fields
+ // nativeTrackInJavaObj
+ fields.fidNativeVisualizer = env->GetFieldID(
+ fields.clazzEffect,
+ "mNativeVisualizer", "I");
+ if (fields.fidNativeVisualizer == NULL) {
+ LOGE("Can't find Visualizer.%s", "mNativeVisualizer");
+ return;
+ }
+ // fidJniData;
+ fields.fidJniData = env->GetFieldID(
+ fields.clazzEffect,
+ "mJniData", "I");
+ if (fields.fidJniData == NULL) {
+ LOGE("Can't find Visualizer.%s", "mJniData");
+ return;
+ }
+
+}
+
+
+static jint
+android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
+ jint sessionId, jintArray jId)
+{
+ LOGV("android_media_visualizer_native_setup");
+ visualizerJniStorage* lpJniStorage = NULL;
+ int lStatus = VISUALIZER_ERROR_NO_MEMORY;
+ Visualizer* lpVisualizer = NULL;
+ jint* nId = NULL;
+
+ lpJniStorage = new visualizerJniStorage();
+ if (lpJniStorage == NULL) {
+ LOGE("setup: Error creating JNI Storage");
+ goto setup_failure;
+ }
+
+ lpJniStorage->mCallbackData.visualizer_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
+ // we use a weak reference so the Visualizer object can be garbage collected.
+ lpJniStorage->mCallbackData.visualizer_ref = env->NewGlobalRef(weak_this);
+
+ LOGV("setup: lpJniStorage: %p visualizer_ref %p visualizer_class %p, &mCallbackData %p",
+ lpJniStorage,
+ lpJniStorage->mCallbackData.visualizer_ref,
+ lpJniStorage->mCallbackData.visualizer_class,
+ &lpJniStorage->mCallbackData);
+
+ if (jId) {
+ nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
+ if (nId == NULL) {
+ LOGE("setup: Error retrieving id pointer");
+ lStatus = VISUALIZER_ERROR_BAD_VALUE;
+ goto setup_failure;
+ }
+ } else {
+ LOGE("setup: NULL java array for id pointer");
+ lStatus = VISUALIZER_ERROR_BAD_VALUE;
+ goto setup_failure;
+ }
+
+ // create the native Visualizer object
+ lpVisualizer = new Visualizer(0,
+ NULL,
+ NULL,
+ sessionId);
+ if (lpVisualizer == NULL) {
+ LOGE("Error creating Visualizer");
+ goto setup_failure;
+ }
+
+ lStatus = translateError(lpVisualizer->initCheck());
+ if (lStatus != VISUALIZER_SUCCESS && lStatus != VISUALIZER_ERROR_ALREADY_EXISTS) {
+ LOGE("Visualizer initCheck failed %d", lStatus);
+ goto setup_failure;
+ }
+
+ nId[0] = lpVisualizer->id();
+
+ env->ReleasePrimitiveArrayCritical(jId, nId, 0);
+ nId = NULL;
+
+ env->SetIntField(thiz, fields.fidNativeVisualizer, (int)lpVisualizer);
+
+ env->SetIntField(thiz, fields.fidJniData, (int)lpJniStorage);
+
+ return VISUALIZER_SUCCESS;
+
+ // failures:
+setup_failure:
+
+ if (nId != NULL) {
+ env->ReleasePrimitiveArrayCritical(jId, nId, 0);
+ }
+
+ if (lpVisualizer) {
+ delete lpVisualizer;
+ }
+ env->SetIntField(thiz, fields.fidNativeVisualizer, 0);
+
+ if (lpJniStorage) {
+ delete lpJniStorage;
+ }
+ env->SetIntField(thiz, fields.fidJniData, 0);
+
+ return lStatus;
+}
+
+// ----------------------------------------------------------------------------
+static void android_media_visualizer_native_finalize(JNIEnv *env, jobject thiz) {
+ LOGV("android_media_visualizer_native_finalize jobject: %x\n", (int)thiz);
+
+ // delete the Visualizer object
+ Visualizer* lpVisualizer = (Visualizer *)env->GetIntField(
+ thiz, fields.fidNativeVisualizer);
+ if (lpVisualizer) {
+ LOGV("deleting Visualizer: %x\n", (int)lpVisualizer);
+ delete lpVisualizer;
+ }
+
+ // delete the JNI data
+ visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(
+ thiz, fields.fidJniData);
+ if (lpJniStorage) {
+ LOGV("deleting pJniStorage: %x\n", (int)lpJniStorage);
+ delete lpJniStorage;
+ }
+}
+
+// ----------------------------------------------------------------------------
+static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) {
+
+ // do everything a call to finalize would
+ android_media_visualizer_native_finalize(env, thiz);
+ // + reset the native resources in the Java object so any attempt to access
+ // them after a call to release fails.
+ env->SetIntField(thiz, fields.fidNativeVisualizer, 0);
+ env->SetIntField(thiz, fields.fidJniData, 0);
+}
+
+static jint
+android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return VISUALIZER_ERROR_NO_INIT;
+ }
+
+ return translateError(lpVisualizer->setEnabled(enabled));
+}
+
+static jboolean
+android_media_visualizer_native_getEnabled(JNIEnv *env, jobject thiz)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return false;
+ }
+
+ return (jboolean)lpVisualizer->getEnabled();
+}
+
+static jintArray
+android_media_visualizer_native_getCaptureSizeRange(JNIEnv *env, jobject thiz)
+{
+ jintArray jRange = env->NewIntArray(2);
+ jint *nRange = env->GetIntArrayElements(jRange, NULL);
+ nRange[0] = Visualizer::getMinCaptureSize();
+ nRange[1] = Visualizer::getMaxCaptureSize();
+ LOGV("getCaptureSizeRange() min %d max %d", nRange[0], nRange[1]);
+ env->ReleaseIntArrayElements(jRange, nRange, 0);
+ return jRange;
+}
+
+static jint
+android_media_visualizer_native_getMaxCaptureRate(JNIEnv *env, jobject thiz)
+{
+ return Visualizer::getMaxCaptureRate();
+}
+
+static jint
+android_media_visualizer_native_setCaptureSize(JNIEnv *env, jobject thiz, jint size)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return VISUALIZER_ERROR_NO_INIT;
+ }
+
+ return translateError(lpVisualizer->setCaptureSize(size));
+}
+
+static jint
+android_media_visualizer_native_getCaptureSize(JNIEnv *env, jobject thiz)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return -1;
+ }
+ return lpVisualizer->getCaptureSize();
+}
+
+static jint
+android_media_visualizer_native_getSamplingRate(JNIEnv *env, jobject thiz)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return -1;
+ }
+ return lpVisualizer->getSamplingRate();
+}
+
+static jint
+android_media_visualizer_native_getWaveForm(JNIEnv *env, jobject thiz, jbyteArray jWaveform)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return VISUALIZER_ERROR_NO_INIT;
+ }
+
+ jbyte* nWaveform = (jbyte *) env->GetPrimitiveArrayCritical(jWaveform, NULL);
+ if (nWaveform == NULL) {
+ return VISUALIZER_ERROR_NO_MEMORY;
+ }
+ jint status = translateError(lpVisualizer->getWaveForm((uint8_t *)nWaveform));
+
+ env->ReleasePrimitiveArrayCritical(jWaveform, nWaveform, 0);
+
+ return status;
+}
+
+static jint
+android_media_visualizer_native_getFft(JNIEnv *env, jobject thiz, jbyteArray jFft)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return VISUALIZER_ERROR_NO_INIT;
+ }
+
+ jbyte* nFft = (jbyte *) env->GetPrimitiveArrayCritical(jFft, NULL);
+ if (nFft == NULL) {
+ return VISUALIZER_ERROR_NO_MEMORY;
+ }
+ jint status = translateError(lpVisualizer->getFft((uint8_t *)nFft));
+
+ env->ReleasePrimitiveArrayCritical(jFft, nFft, 0);
+
+ return status;
+}
+
+static jint
+android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean jWaveform, jboolean jFft)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return VISUALIZER_ERROR_NO_INIT;
+ }
+ visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(thiz,
+ fields.fidJniData);
+ if (lpJniStorage == NULL) {
+ return VISUALIZER_ERROR_NO_INIT;
+ }
+
+ LOGV("setPeriodicCapture: rate %d, jWaveform %d jFft %d",
+ rate,
+ jWaveform,
+ jFft);
+
+ uint32_t flags = Visualizer::CAPTURE_CALL_JAVA;
+ if (jWaveform) flags |= Visualizer::CAPTURE_WAVEFORM;
+ if (jFft) flags |= Visualizer::CAPTURE_FFT;
+ Visualizer::capture_cbk_t cbk = captureCallback;
+ if (!jWaveform && !jFft) cbk = NULL;
+
+ return translateError(lpVisualizer->setCaptureCallBack(cbk,
+ &lpJniStorage->mCallbackData,
+ flags,
+ rate));
+}
+
+// ----------------------------------------------------------------------------
+
+// Dalvik VM type signatures
+static JNINativeMethod gMethods[] = {
+ {"native_init", "()V", (void *)android_media_visualizer_native_init},
+ {"native_setup", "(Ljava/lang/Object;I[I)I",
+ (void *)android_media_visualizer_native_setup},
+ {"native_finalize", "()V", (void *)android_media_visualizer_native_finalize},
+ {"native_release", "()V", (void *)android_media_visualizer_native_release},
+ {"native_setEnabled", "(Z)I", (void *)android_media_visualizer_native_setEnabled},
+ {"native_getEnabled", "()Z", (void *)android_media_visualizer_native_getEnabled},
+ {"getCaptureSizeRange", "()[I", (void *)android_media_visualizer_native_getCaptureSizeRange},
+ {"getMaxCaptureRate", "()I", (void *)android_media_visualizer_native_getMaxCaptureRate},
+ {"native_setCaptureSize", "(I)I", (void *)android_media_visualizer_native_setCaptureSize},
+ {"native_getCaptureSize", "()I", (void *)android_media_visualizer_native_getCaptureSize},
+ {"native_getSamplingRate", "()I", (void *)android_media_visualizer_native_getSamplingRate},
+ {"native_getWaveForm", "([B)I", (void *)android_media_visualizer_native_getWaveForm},
+ {"native_getFft", "([B)I", (void *)android_media_visualizer_native_getFft},
+ {"native_setPeriodicCapture","(IZZ)I",(void *)android_media_setPeriodicCapture},
+};
+
+// ----------------------------------------------------------------------------
+
+int register_android_media_visualizer(JNIEnv *env)
+{
+ return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
diff --git a/media/libeffects/Android.mk b/media/libeffects/Android.mk
index b5f1d42..54e87f3 100644
--- a/media/libeffects/Android.mk
+++ b/media/libeffects/Android.mk
@@ -94,3 +94,33 @@ LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
endif
+
+
+# Visualizer library
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ EffectVisualizer.cpp
+
+LOCAL_CFLAGS+= -O2
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE:= libvisualizer
+
+ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
+LOCAL_LDLIBS += -ldl
+endif
+
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, graphics corecg)
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libeffects/EffectVisualizer.cpp b/media/libeffects/EffectVisualizer.cpp
new file mode 100644
index 0000000..f27e296
--- /dev/null
+++ b/media/libeffects/EffectVisualizer.cpp
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 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_TAG "Visualizer"
+//#define LOG_NDEBUG 0
+#include <cutils/log.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <new>
+#include <media/EffectVisualizerApi.h>
+
+namespace android {
+
+// effect_interface_t interface implementation for visualizer effect
+extern "C" const struct effect_interface_s gVisualizerInterface;
+
+// Google Visualizer UUID: d069d9e0-8329-11df-9168-0002a5d5c51b
+const effect_descriptor_t gVisualizerDescriptor = {
+ {0xe46b26a0, 0xdddd, 0x11db, 0x8afd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type
+ {0xd069d9e0, 0x8329, 0x11df, 0x9168, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
+ EFFECT_API_VERSION,
+ (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
+ 0, // TODO
+ 1,
+ "Visualizer",
+ "Google Inc.",
+};
+
+enum visualizer_state_e {
+ VISUALIZER_STATE_UNINITIALIZED,
+ VISUALIZER_STATE_INITIALIZED,
+ VISUALIZER_STATE_ACTIVE,
+};
+
+struct VisualizerContext {
+ const struct effect_interface_s *mItfe;
+ effect_config_t mConfig;
+ uint32_t mState;
+ uint32_t mCaptureIdx;
+ uint32_t mCaptureSize;
+ uint32_t mCurrentBuf;
+ uint8_t mCaptureBuf[2][VISUALIZER_CAPTURE_SIZE_MAX];
+};
+
+
+//
+//--- Local functions
+//
+
+void Visualizer_reset(VisualizerContext *pContext)
+{
+ pContext->mCaptureIdx = 0;
+ pContext->mCurrentBuf = 0;
+ memset(pContext->mCaptureBuf[0], 0, VISUALIZER_CAPTURE_SIZE_MAX);
+ memset(pContext->mCaptureBuf[1], 0, VISUALIZER_CAPTURE_SIZE_MAX);
+}
+
+//----------------------------------------------------------------------------
+// Visualizer_configure()
+//----------------------------------------------------------------------------
+// Purpose: Set input and output audio configuration.
+//
+// Inputs:
+// pContext: effect engine context
+// pConfig: pointer to effect_config_t structure holding input and output
+// configuration parameters
+//
+// Outputs:
+//
+//----------------------------------------------------------------------------
+
+int Visualizer_configure(VisualizerContext *pContext, effect_config_t *pConfig)
+{
+ LOGV("Visualizer_configure start");
+
+ if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL;
+ if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL;
+ if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL;
+ if (pConfig->inputCfg.channels != CHANNEL_STEREO) return -EINVAL;
+ if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
+ pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
+ if (pConfig->inputCfg.format != SAMPLE_FORMAT_PCM_S15) return -EINVAL;
+
+ memcpy(&pContext->mConfig, pConfig, sizeof(effect_config_t));
+
+ Visualizer_reset(pContext);
+
+ return 0;
+}
+
+
+//----------------------------------------------------------------------------
+// Visualizer_init()
+//----------------------------------------------------------------------------
+// Purpose: Initialize engine with default configuration.
+//
+// Inputs:
+// pContext: effect engine context
+//
+// Outputs:
+//
+//----------------------------------------------------------------------------
+
+int Visualizer_init(VisualizerContext *pContext)
+{
+ pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+ pContext->mConfig.inputCfg.channels = CHANNEL_STEREO;
+ pContext->mConfig.inputCfg.format = SAMPLE_FORMAT_PCM_S15;
+ pContext->mConfig.inputCfg.samplingRate = 44100;
+ pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL;
+ pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
+ pContext->mConfig.inputCfg.bufferProvider.cookie = NULL;
+ pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
+ pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
+ pContext->mConfig.outputCfg.channels = CHANNEL_STEREO;
+ pContext->mConfig.outputCfg.format = SAMPLE_FORMAT_PCM_S15;
+ pContext->mConfig.outputCfg.samplingRate = 44100;
+ pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL;
+ pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
+ pContext->mConfig.outputCfg.bufferProvider.cookie = NULL;
+ pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
+
+ pContext->mCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX;
+
+ Visualizer_configure(pContext, &pContext->mConfig);
+
+ return 0;
+}
+
+//
+//--- Effect Library Interface Implementation
+//
+
+extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects) {
+ *pNumEffects = 1;
+ return 0;
+}
+
+extern "C" int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) {
+ if (pDescriptor == NULL) {
+ return -EINVAL;
+ }
+ if (index > 0) {
+ return -EINVAL;
+ }
+ memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t));
+ return 0;
+}
+
+extern "C" int EffectCreate(effect_uuid_t *uuid,
+ int32_t sessionId,
+ int32_t ioId,
+ effect_interface_t *pInterface) {
+ int ret;
+ int i;
+
+ if (pInterface == NULL || uuid == NULL) {
+ return -EINVAL;
+ }
+
+ if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) != 0) {
+ return -EINVAL;
+ }
+
+ VisualizerContext *pContext = new VisualizerContext;
+
+ pContext->mItfe = &gVisualizerInterface;
+ pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
+
+ ret = Visualizer_init(pContext);
+ if (ret < 0) {
+ LOGW("EffectCreate() init failed");
+ delete pContext;
+ return ret;
+ }
+
+ *pInterface = (effect_interface_t)pContext;
+
+ pContext->mState = VISUALIZER_STATE_INITIALIZED;
+
+ LOGV("EffectCreate %p", pContext);
+
+ return 0;
+
+}
+
+extern "C" int EffectRelease(effect_interface_t interface) {
+ VisualizerContext * pContext = (VisualizerContext *)interface;
+
+ LOGV("EffectRelease %p", interface);
+ if (pContext == NULL) {
+ return -EINVAL;
+ }
+ pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
+ delete pContext;
+
+ return 0;
+}
+
+//
+//--- Effect Control Interface Implementation
+//
+
+static inline int16_t clamp16(int32_t sample)
+{
+ if ((sample>>15) ^ (sample>>31))
+ sample = 0x7FFF ^ (sample>>31);
+ return sample;
+}
+
+extern "C" int Visualizer_process(
+ effect_interface_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
+{
+ android::VisualizerContext * pContext = (android::VisualizerContext *)self;
+
+ if (pContext == NULL) {
+ return -EINVAL;
+ }
+ if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
+ return -ENOSYS;
+ }
+
+ if (inBuffer == NULL || inBuffer->raw == NULL ||
+ outBuffer == NULL || outBuffer->raw == NULL ||
+ inBuffer->frameCount != outBuffer->frameCount ||
+ inBuffer->frameCount == 0) {
+ return -EINVAL;
+ }
+
+ // all code below assumes stereo 16 bit PCM output and input
+ uint32_t captIdx;
+ uint32_t inIdx;
+ uint8_t *buf = pContext->mCaptureBuf[pContext->mCurrentBuf];
+ for (inIdx = 0, captIdx = pContext->mCaptureIdx;
+ inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize;
+ inIdx++, captIdx++) {
+ int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
+ smp = (smp + (1 << 8)) >> 9;
+ buf[captIdx] = ((uint8_t)smp)^0x80;
+ }
+ pContext->mCaptureIdx = captIdx;
+
+ // go to next buffer when buffer full
+ if (pContext->mCaptureIdx == pContext->mCaptureSize) {
+ pContext->mCurrentBuf ^= 1;
+ pContext->mCaptureIdx = 0;
+ }
+
+ if (inBuffer->raw != outBuffer->raw) {
+ if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+ for (size_t i = 0; i < outBuffer->frameCount*2; i++) {
+ outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]);
+ }
+ } else {
+ memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t));
+ }
+ }
+ return 0;
+} // end Visualizer_process
+
+extern "C" int Visualizer_command(effect_interface_t self, int cmdCode, int cmdSize,
+ void *pCmdData, int *replySize, void *pReplyData) {
+
+ android::VisualizerContext * pContext = (android::VisualizerContext *)self;
+ int retsize;
+
+ if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) {
+ return -EINVAL;
+ }
+
+// LOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize);
+
+ switch (cmdCode) {
+ case EFFECT_CMD_INIT:
+ if (pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ *(int *) pReplyData = Visualizer_init(pContext);
+ break;
+ case EFFECT_CMD_CONFIGURE:
+ if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
+ || pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ *(int *) pReplyData = Visualizer_configure(pContext,
+ (effect_config_t *) pCmdData);
+ break;
+ case EFFECT_CMD_RESET:
+ Visualizer_reset(pContext);
+ break;
+ case EFFECT_CMD_ENABLE:
+ if (pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ if (pContext->mState != VISUALIZER_STATE_INITIALIZED) {
+ return -ENOSYS;
+ }
+ pContext->mState = VISUALIZER_STATE_ACTIVE;
+ LOGV("EFFECT_CMD_ENABLE() OK");
+ *(int *)pReplyData = 0;
+ break;
+ case EFFECT_CMD_DISABLE:
+ if (pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
+ return -ENOSYS;
+ }
+ pContext->mState = VISUALIZER_STATE_INITIALIZED;
+ LOGV("EFFECT_CMD_DISABLE() OK");
+ *(int *)pReplyData = 0;
+ break;
+ case EFFECT_CMD_GET_PARAM: {
+ if (pCmdData == NULL ||
+ cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
+ pReplyData == NULL ||
+ *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
+ return -EINVAL;
+ }
+ memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
+ effect_param_t *p = (effect_param_t *)pReplyData;
+ p->status = 0;
+ *replySize = sizeof(effect_param_t) + sizeof(uint32_t);
+ if (p->psize != sizeof(uint32_t) ||
+ *(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) {
+ p->status = -EINVAL;
+ break;
+ }
+ LOGV("get mCaptureSize = %d", pContext->mCaptureSize);
+ *((uint32_t *)p->data + 1) = pContext->mCaptureSize;
+ p->vsize = sizeof(uint32_t);
+ *replySize += sizeof(uint32_t);
+ } break;
+ case EFFECT_CMD_SET_PARAM: {
+ if (pCmdData == NULL ||
+ cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
+ pReplyData == NULL || *replySize != sizeof(int32_t)) {
+ return -EINVAL;
+ }
+ *(int32_t *)pReplyData = 0;
+ effect_param_t *p = (effect_param_t *)pCmdData;
+ if (p->psize != sizeof(uint32_t) ||
+ p->vsize != sizeof(uint32_t) ||
+ *(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) {
+ *(int32_t *)pReplyData = -EINVAL;
+ break;;
+ }
+ pContext->mCaptureSize = *((uint32_t *)p->data + 1);
+ LOGV("set mCaptureSize = %d", pContext->mCaptureSize);
+ } break;
+ case EFFECT_CMD_SET_DEVICE:
+ case EFFECT_CMD_SET_VOLUME:
+ case EFFECT_CMD_SET_AUDIO_MODE:
+ break;
+
+
+ case VISU_CMD_CAPTURE:
+ if (pReplyData == NULL || *replySize != (int)pContext->mCaptureSize) {
+ LOGV("VISU_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d",
+ *replySize, pContext->mCaptureSize);
+ return -EINVAL;
+ }
+ if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
+ memcpy(pReplyData,
+ pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
+ pContext->mCaptureSize);
+ } else {
+ memset(pReplyData, 0x80, pContext->mCaptureSize);
+ }
+ break;
+
+ default:
+ LOGW("Visualizer_command invalid command %d",cmdCode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+// effect_interface_t interface implementation for visualizer effect
+const struct effect_interface_s gVisualizerInterface = {
+ Visualizer_process,
+ Visualizer_command
+};
+
+} // namespace
+
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index de9e51d..977e6be 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -30,7 +30,8 @@ LOCAL_SRC_FILES:= \
MediaProfiles.cpp \
IEffect.cpp \
IEffectClient.cpp \
- AudioEffect.cpp
+ AudioEffect.cpp \
+ Visualizer.cpp
LOCAL_SHARED_LIBRARIES := \
libui libcutils libutils libbinder libsonivox libicuuc libexpat libsurfaceflinger_client libcamera_client
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 4afa2dc..783249d 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -171,7 +171,7 @@ AudioEffect::~AudioEffect()
LOGV("Destructor %p", this);
if (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS) {
- disable();
+ setEnabled(false);
if (mIEffect != NULL) {
mIEffect->disconnect();
mIEffect->asBinder()->unlinkToDeath(mIEffectClient);
@@ -196,36 +196,28 @@ effect_descriptor_t AudioEffect::descriptor() const
return mDescriptor;
}
-bool AudioEffect::isEnabled() const
+bool AudioEffect::getEnabled() const
{
return (mEnabled != 0);
}
-status_t AudioEffect::enable()
+status_t AudioEffect::setEnabled(bool enabled)
{
if (mStatus != NO_ERROR) {
return INVALID_OPERATION;
}
- LOGV("enable %p", this);
- if (android_atomic_or(1, &mEnabled) == 0) {
- return mIEffect->enable();
- }
-
- return INVALID_OPERATION;
-}
-
-status_t AudioEffect::disable()
-{
- if (mStatus != NO_ERROR) {
- return INVALID_OPERATION;
- }
- LOGV("disable %p", this);
-
- if (android_atomic_and(~1, &mEnabled) == 1) {
- return mIEffect->disable();
+ if (enabled) {
+ LOGV("enable %p", this);
+ if (android_atomic_or(1, &mEnabled) == 0) {
+ return mIEffect->enable();
+ }
+ } else {
+ LOGV("disable %p", this);
+ if (android_atomic_and(~1, &mEnabled) == 1) {
+ return mIEffect->disable();
+ }
}
-
return INVALID_OPERATION;
}
@@ -349,7 +341,7 @@ void AudioEffect::controlStatusChanged(bool controlGranted)
void AudioEffect::enableStatusChanged(bool enabled)
{
- LOGV("enableStatusChanged %p enabled %d", this, enabled);
+ LOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf);
if (mStatus == ALREADY_EXISTS) {
mEnabled = enabled;
if (mCbf) {
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index 1ae222e..4abfa75 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -35,8 +35,7 @@ enum {
DECODE_FD,
CREATE_MEDIA_RECORDER,
CREATE_METADATA_RETRIEVER,
- GET_OMX,
- SNOOP
+ GET_OMX
};
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
@@ -134,14 +133,6 @@ public:
return interface_cast<IMemory>(reply.readStrongBinder());
}
- virtual sp<IMemory> snoop()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
- remote()->transact(SNOOP, data, &reply);
- return interface_cast<IMemory>(reply.readStrongBinder());
- }
-
virtual sp<IOMX> getOMX() {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
@@ -221,12 +212,6 @@ status_t BnMediaPlayerService::onTransact(
reply->writeStrongBinder(player->asBinder());
return NO_ERROR;
} break;
- case SNOOP: {
- CHECK_INTERFACE(IMediaPlayerService, data, reply);
- sp<IMemory> snooped_audio = snoop();
- reply->writeStrongBinder(snooped_audio->asBinder());
- return NO_ERROR;
- } break;
case CREATE_MEDIA_RECORDER: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
pid_t pid = data.readInt32();
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
new file mode 100644
index 0000000..47e96e5
--- /dev/null
+++ b/media/libmedia/Visualizer.cpp
@@ -0,0 +1,330 @@
+/*
+**
+** 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) {
+ int32_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
+
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index d5a3c13..b43f75f 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -658,61 +658,4 @@ void MediaPlayer::died()
}
-extern "C" {
-#define FLOATING_POINT 1
-#include "fftwrap.h"
-}
-
-static void *ffttable = NULL;
-
-// peeks at the audio data and fills 'data' with the requested kind
-// (currently kind=0 returns mono 16 bit PCM data, and kind=1 returns
-// 256 point FFT data). Return value is number of samples returned,
-// which may be 0.
-/*static*/ int MediaPlayer::snoop(short* data, int len, int kind) {
-
- sp<IMemory> p;
- const sp<IMediaPlayerService>& service = getMediaPlayerService();
- if (service != 0) {
- // Take a peek at the waveform. The returned data consists of 16 bit mono PCM data.
- p = service->snoop();
-
- if (p == NULL) {
- return 0;
- }
-
- if (kind == 0) { // return waveform data
- int plen = p->size();
- len *= 2; // number of shorts -> number of bytes
- short *src = (short*) p->pointer();
- if (plen > len) {
- plen = len;
- }
- memcpy(data, src, plen);
- return plen / sizeof(short); // return number of samples
- } else if (kind == 1) {
- // TODO: use a more efficient FFT
- // Right now this uses the speex library, which is compiled to do a float FFT
- if (!ffttable) ffttable = spx_fft_init(512);
- short *usrc = (short*) p->pointer();
- float fsrc[512];
- for (int i=0;i<512;i++)
- fsrc[i] = usrc[i];
- float fdst[512];
- spx_fft_float(ffttable, fsrc, fdst);
- if (len > 512) {
- len = 512;
- }
- len /= 2; // only half the output data is valid
- for (int i=0; i < len; i++)
- data[i] = fdst[i];
- return len;
- }
-
- } else {
- LOGE("Unable to locate media service");
- }
- return 0;
-}
-
}; // namespace android
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 82d5c14..4872047 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1265,98 +1265,6 @@ Exit:
return mem;
}
-/*
- * Avert your eyes, ugly hack ahead.
- * The following is to support music visualizations.
- */
-
-static const int NUMVIZBUF = 32;
-static const int VIZBUFFRAMES = 1024;
-static const int BUFTIMEMSEC = NUMVIZBUF * VIZBUFFRAMES * 1000 / 44100;
-static const int TOTALBUFTIMEMSEC = NUMVIZBUF * BUFTIMEMSEC;
-
-static bool gotMem = false;
-static sp<MemoryHeapBase> heap;
-static sp<MemoryBase> mem[NUMVIZBUF];
-static uint64_t endTime;
-static uint64_t lastReadTime;
-static uint64_t lastWriteTime;
-static int writeIdx = 0;
-
-static void allocVizBufs() {
- if (!gotMem) {
- heap = new MemoryHeapBase(NUMVIZBUF * VIZBUFFRAMES * 2, 0, "snooper");
- for (int i=0;i<NUMVIZBUF;i++) {
- mem[i] = new MemoryBase(heap, VIZBUFFRAMES * 2 * i, VIZBUFFRAMES * 2);
- }
- endTime = 0;
- gotMem = true;
- }
-}
-
-
-/*
- * Get a buffer of audio data that is about to be played.
- * We don't synchronize this because in practice the writer
- * is ahead of the reader, and even if we did happen to catch
- * a buffer while it's being written, it's just a visualization,
- * so no harm done.
- */
-static sp<MemoryBase> getVizBuffer() {
-
- allocVizBufs();
-
- lastReadTime = uptimeMillis();
-
- // if there is no recent buffer (yet), just return empty handed
- if (lastWriteTime + TOTALBUFTIMEMSEC < lastReadTime) {
- //LOGI("@@@@ no audio data to look at yet: %d + %d < %d", (int)lastWriteTime, TOTALBUFTIMEMSEC, (int)lastReadTime);
- return NULL;
- }
-
- int timedelta = endTime - lastReadTime;
- if (timedelta < 0) timedelta = 0;
- int framedelta = timedelta * 44100 / 1000;
- int headIdx = (writeIdx - framedelta) / VIZBUFFRAMES - 1;
- while (headIdx < 0) {
- headIdx += NUMVIZBUF;
- }
- return mem[headIdx];
-}
-
-// Append the data to the vizualization buffer
-static void makeVizBuffers(const char *data, int len, uint64_t time) {
-
- allocVizBufs();
-
- uint64_t startTime = time;
- const int frameSize = 4; // 16 bit stereo sample is 4 bytes
- int offset = writeIdx;
- int maxoff = heap->getSize() / 2; // in shorts
- short *base = (short*)heap->getBase();
- short *src = (short*)data;
- while (len > 0) {
-
- // Degrade quality by mixing to mono and clearing the lowest 3 bits.
- // This should still be good enough for a visualization
- base[offset++] = ((int(src[0]) + int(src[1])) >> 1) & ~0x7;
- src += 2;
- len -= frameSize;
- if (offset >= maxoff) {
- offset = 0;
- }
- }
- writeIdx = offset;
- endTime = time + (len / frameSize) / 44;
- //LOGI("@@@ stored buffers from %d to %d", uint32_t(startTime), uint32_t(time));
-}
-
-sp<IMemory> MediaPlayerService::snoop()
-{
- sp<MemoryBase> mem = getVizBuffer();
- return mem;
-}
-
#undef LOG_TAG
#define LOG_TAG "AudioSink"
@@ -1371,7 +1279,6 @@ MediaPlayerService::AudioOutput::AudioOutput(int sessionId)
mRightVolume = 1.0;
mLatency = 0;
mMsecsPerFrame = 0;
- mNumFramesWritten = 0;
setMinBufferCount();
}
@@ -1516,30 +1423,9 @@ void MediaPlayerService::AudioOutput::start()
if (mTrack) {
mTrack->setVolume(mLeftVolume, mRightVolume);
mTrack->start();
- mTrack->getPosition(&mNumFramesWritten);
}
}
-void MediaPlayerService::AudioOutput::snoopWrite(const void* buffer, size_t size) {
- // Only make visualization buffers if anyone recently requested visualization data
- uint64_t now = uptimeMillis();
- if (lastReadTime + TOTALBUFTIMEMSEC >= now) {
- // Based on the current play counter, the number of frames written and
- // the current real time we can calculate the approximate real start
- // time of the buffer we're about to write.
- uint32_t pos;
- mTrack->getPosition(&pos);
-
- // we're writing ahead by this many frames:
- int ahead = mNumFramesWritten - pos;
- //LOGI("@@@ written: %d, playpos: %d, latency: %d", mNumFramesWritten, pos, mTrack->latency());
- // which is this many milliseconds, assuming 44100 Hz:
- ahead /= 44;
-
- makeVizBuffers((const char*)buffer, size, now + ahead + mTrack->latency());
- lastWriteTime = now;
- }
-}
ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
@@ -1548,9 +1434,7 @@ ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
//LOGV("write(%p, %u)", buffer, size);
if (mTrack) {
- snoopWrite(buffer, size);
ssize_t ret = mTrack->write(buffer, size);
- mNumFramesWritten += ret / 4; // assume 16 bit stereo
return ret;
}
return NO_INIT;
@@ -1560,7 +1444,6 @@ void MediaPlayerService::AudioOutput::stop()
{
LOGV("stop");
if (mTrack) mTrack->stop();
- lastWriteTime = 0;
}
void MediaPlayerService::AudioOutput::flush()
@@ -1573,7 +1456,6 @@ void MediaPlayerService::AudioOutput::pause()
{
LOGV("pause");
if (mTrack) mTrack->pause();
- lastWriteTime = 0;
}
void MediaPlayerService::AudioOutput::close()
@@ -1609,9 +1491,6 @@ void MediaPlayerService::AudioOutput::CallbackWrapper(
buffer->size = actualSize;
- if (actualSize > 0) {
- me->snoopWrite(buffer->raw, actualSize);
- }
}
#undef LOG_TAG
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 60b91c6..39f525e 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -113,9 +113,6 @@ class MediaPlayerService : public BnMediaPlayerService
static bool mIsOnEmulator;
static int mMinBufferCount; // 12 for emulator; otherwise 4
- public: // visualization hack support
- uint32_t mNumFramesWritten;
- void snoopWrite(const void*, size_t);
};
class AudioCache : public MediaPlayerBase::AudioSink
@@ -191,7 +188,6 @@ public:
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length, int audioSessionId);
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
- virtual sp<IMemory> snoop();
virtual sp<IOMX> getOMX();
virtual status_t dump(int fd, const Vector<String16>& args);