am 7070b365: Added support for auxiliary audio effects to AudioTrack and MediaPlayer.

Merge commit '7070b36549d511d6627538c73dfbab23fbae5b43' into gingerbread-plus-aosp

* commit '7070b36549d511d6627538c73dfbab23fbae5b43':
  Added support for auxiliary audio effects to AudioTrack and MediaPlayer.
This commit is contained in:
Eric Laurent
2010-07-21 09:49:27 -07:00
committed by Android Git Automerger
12 changed files with 299 additions and 9 deletions

View File

@@ -360,6 +360,7 @@ android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
if (lpTrack == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for start()");
return;
}
lpTrack->start();
@@ -375,6 +376,7 @@ android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
if (lpTrack == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for stop()");
return;
}
lpTrack->stop();
@@ -390,6 +392,7 @@ android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
if (lpTrack == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for pause()");
return;
}
lpTrack->pause();
@@ -405,6 +408,7 @@ android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
if (lpTrack == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for flush()");
return;
}
lpTrack->flush();
@@ -419,6 +423,7 @@ android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, j
if (lpTrack == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for setVolume()");
return;
}
lpTrack->setVolume(leftVol, rightVol);
@@ -515,6 +520,7 @@ static jint android_media_AudioTrack_native_write(JNIEnv *env, jobject thiz,
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for write()");
return 0;
}
// get the pointer for the audio data from the java array
@@ -801,6 +807,36 @@ static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thi
return minBuffSize;
}
// ----------------------------------------------------------------------------
static void
android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level )
{
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()");
return;
}
lpTrack->setAuxEffectSendLevel(level);
}
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env, jobject thiz,
jint effectId) {
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack) {
return android_media_translateErrorCode( lpTrack->attachAuxEffect(effectId) );
} else {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for attachAuxEffect()");
return AUDIOTRACK_ERROR;
}
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
@@ -837,6 +873,10 @@ static JNINativeMethod gMethods[] = {
"(I)I", (void *)android_media_AudioTrack_get_output_sample_rate},
{"native_get_min_buff_size",
"(III)I", (void *)android_media_AudioTrack_get_min_buff_size},
{"native_setAuxEffectSendLevel",
"(F)V", (void *)android_media_AudioTrack_setAuxEffectSendLevel},
{"native_attachAuxEffect",
"(I)I", (void *)android_media_AudioTrack_attachAuxEffect},
};

View File

@@ -261,8 +261,8 @@ public:
/* set the send level for this track. An auxiliary effect should be attached
* to the track with attachEffect(). Level must be <= 1.0.
*/
status_t setSendLevel(float level);
void getSendLevel(float* level);
status_t setAuxEffectSendLevel(float level);
void getAuxEffectSendLevel(float* level);
/* set sample rate for this track, mostly used for games' sound effects
*/
@@ -479,6 +479,7 @@ private:
uint32_t mUpdatePeriod;
uint32_t mFlags;
int mSessionId;
int mAuxEffectId;
};

View File

@@ -48,6 +48,8 @@ public:
virtual status_t setVolume(float leftVolume, float rightVolume) = 0;
virtual status_t suspend() = 0;
virtual status_t resume() = 0;
virtual status_t setAuxEffectSendLevel(float level) = 0;
virtual status_t attachAuxEffect(int effectId) = 0;
// Invoke a generic method on the player by using opaque parcels
// for the request and reply.

View File

@@ -173,6 +173,8 @@ public:
status_t resume();
status_t setAudioSessionId(int sessionId);
int getAudioSessionId();
status_t setAuxEffectSendLevel(float level);
status_t attachAuxEffect(int effectId);
private:
void clear_l();
status_t seekTo_l(int msec);
@@ -200,6 +202,7 @@ private:
int mVideoWidth;
int mVideoHeight;
int mAudioSessionId;
float mSendLevel;
};
}; // namespace android

View File

@@ -963,6 +963,65 @@ public class AudioTrack
return native_reload_static();
}
//--------------------------------------------------------------------------
// Audio effects management
//--------------------
/**
* Attaches an auxiliary effect to the audio track. A typical auxiliary effect is a
* reverberation effect which can be applied on any sound source that directs a certain
* amount of its energy to this effect. This amount is defined by setAuxEffectSendLevel().
* {@see #setAuxEffectSendLevel(float)}.
// TODO when AudioEffect are unhidden
* <p>After creating an auxiliary effect (e.g. {_at_link android.media.EnvironmentalReverb}),
* retrieve its ID with {_at_link android.media.AudioEffect#getId()} and use it when calling
* this method to attach the audio track to the effect.
* <p>To detach the effect from the audio track, call this method with a null effect id.
*
* @param effectId system wide unique id of the effect to attach
* @return error code or success, see {@link #SUCCESS},
* {@link #ERROR_INVALID_OPERATION}, {@link #ERROR_BAD_VALUE}
// FIXME: unhide.
* @hide
*/
public int attachAuxEffect(int effectId) {
if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
return native_attachAuxEffect(effectId);
}
/**
* Sets the send level of the audio track to the attached auxiliary effect
* {@see #attachAuxEffect(int)}. The level value range is 0 to 1.0.
* <p>By default the send level is 0, so even if an effect is attached to the player
* this method must be called for the effect to be applied.
* <p>Note that the passed level value is a raw scalar. UI controls should be scaled
* logarithmically: the gain applied by audio framework ranges from -72dB to 0dB,
* so an appropriate conversion from linear UI input x to level is:
* x == 0 -> level = 0
* 0 < x <= R -> level = 10^(72*(x-R)/20/R)
*
* @param level send level scalar
* @return error code or success, see {@link #SUCCESS},
* {@link #ERROR_INVALID_OPERATION}
// FIXME: unhide.
* @hide
*/
public int setAuxEffectSendLevel(float level) {
if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
// clamp the level
if (level < getMinVolume()) {
level = getMinVolume();
}
if (level > getMaxVolume()) {
level = getMaxVolume();
}
native_setAuxEffectSendLevel(level);
return SUCCESS;
}
//---------------------------------------------------------
// Interface definitions
@@ -1123,6 +1182,9 @@ public class AudioTrack
private native final int native_get_session_id();
private native final int native_attachAuxEffect(int effectId);
private native final void native_setAuxEffectSendLevel(float level);
//---------------------------------------------------------
// Utility methods
//------------------

View File

@@ -423,7 +423,7 @@ import java.lang.ref.WeakReference;
* <td>Successful invoke of this method in a valid state transfers the
* object to the <em>Stopped</em> state. Calling this method in an
* invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
* <tr><td>setAudioSessionId </p></td>
* <tr><td>setAudioSessionId </p></td>
* <td>{Idle} </p></td>
* <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
* Error} </p></td>
@@ -434,6 +434,15 @@ import java.lang.ref.WeakReference;
* <td>{} </p></td>
* <td>This method can be called in any state and calling it does not change
* the object state. </p></td></tr>
* <tr><td>attachAuxEffect </p></td>
* <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
* <td>{Idle, Error} </p></td>
* <td>This method must be called after setDataSource.
* Calling it does not change the object state. </p></td></tr>
* <tr><td>setAuxEffectSendLevel </p></td>
* <td>any</p></td>
* <td>{} </p></td>
* <td>Calling this method does not change the object state. </p></td></tr>
*
* </table>
*
@@ -1187,7 +1196,7 @@ public class MediaPlayer
* @throws IllegalStateException if it is called in an invalid state
*
// FIXME: unhide.
// FIXME: link to AudioEffect class when public.
// TODO when AudioEffect is unhidden
* @hide
*/
public native void setAudioSessionId(int sessionId) throws IllegalArgumentException, IllegalStateException;
@@ -1202,6 +1211,41 @@ public class MediaPlayer
*/
public native int getAudioSessionId();
/**
* Attaches an auxiliary effect to the player. A typical auxiliary effect is a reverberation
* effect which can be applied on any sound source that directs a certain amount of its
* energy to this effect. This amount is defined by setAuxEffectSendLevel().
* {@see #setAuxEffectSendLevel(float)}.
// TODO when AudioEffect is unhidden
* <p>After creating an auxiliary effect (e.g. {_at_link android.media.EnvironmentalReverb}),
* retrieve its ID with {_at_link android.media.AudioEffect#getId()} and use it when calling
* this method to attach the player to the effect.
* <p>To detach the effect from the player, call this method with a null effect id.
* <p>This method must be called after one of the overloaded <code> setDataSource </code>
* methods.
*
* @param effectId system wide unique id of the effect to attach
// FIXME: unhide.
* @hide
*/
public native void attachAuxEffect(int effectId);
/**
* Sets the send level of the player to the attached auxiliary effect
* {@see #attachAuxEffect(int)}. The level value range is 0 to 1.0.
* <p>By default the send level is 0, so even if an effect is attached to the player
* this method must be called for the effect to be applied.
* <p>Note that the passed level value is a raw scalar. UI controls should be scaled
* logarithmically: the gain applied by audio framework ranges from -72dB to 0dB,
* so an appropriate conversion from linear UI input x to level is:
* x == 0 -> level = 0
* 0 < x <= R -> level = 10^(72*(x-R)/20/R)
* @param level send level scalar
// FIXME: unhide.
* @hide
*/
public native void setAuxEffectSendLevel(float level);
/**
* @param request Parcel destinated to the media player. The
* Interface token must be set to the IMediaPlayer

View File

@@ -714,6 +714,28 @@ static jint android_media_MediaPlayer_get_audio_session_id(JNIEnv *env, jobject
return mp->getAudioSessionId();
}
static void
android_media_MediaPlayer_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level)
{
LOGV("setAuxEffectSendLevel: level %f", level);
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
process_media_player_call( env, thiz, mp->setAuxEffectSendLevel(level), NULL, NULL );
}
static void android_media_MediaPlayer_attachAuxEffect(JNIEnv *env, jobject thiz, jint effectId) {
LOGV("attachAuxEffect(): %d", sessionId);
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
process_media_player_call( env, thiz, mp->attachAuxEffect(effectId), NULL, NULL );
}
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
@@ -748,6 +770,8 @@ static JNINativeMethod gMethods[] = {
{"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},
{"setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect},
};
static const char* const kClassPathName = "android/media/MediaPlayer";

View File

@@ -209,6 +209,7 @@ status_t AudioTrack::set(
mFrameCount = frameCount;
mNotificationFramesReq = notificationFrames;
mSessionId = sessionId;
mAuxEffectId = 0;
// create the IAudioTrack
status_t status = createTrack(streamType, sampleRate, format, channelCount,
@@ -458,8 +459,9 @@ void AudioTrack::getVolume(float* left, float* right)
}
}
status_t AudioTrack::setSendLevel(float level)
status_t AudioTrack::setAuxEffectSendLevel(float level)
{
LOGV("setAuxEffectSendLevel(%f)", level);
if (level > 1.0f) {
return BAD_VALUE;
}
@@ -471,7 +473,7 @@ status_t AudioTrack::setSendLevel(float level)
return NO_ERROR;
}
void AudioTrack::getSendLevel(float* level)
void AudioTrack::getAuxEffectSendLevel(float* level)
{
if (level != NULL) {
*level = mSendLevel;
@@ -637,7 +639,12 @@ int AudioTrack::getSessionId()
status_t AudioTrack::attachAuxEffect(int effectId)
{
return mAudioTrack->attachAuxEffect(effectId);
LOGV("attachAuxEffect(%d)", effectId);
status_t status = mAudioTrack->attachAuxEffect(effectId);
if (status == NO_ERROR) {
mAuxEffectId = effectId;
}
return status;
}
// -------------------------------------------------------------------------
@@ -752,6 +759,7 @@ status_t AudioTrack::createTrack(
mCblk->volumeLR = (uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000);
mCblk->sendLevel = uint16_t(mSendLevel * 0x1000);
mAudioTrack->attachAuxEffect(mAuxEffectId);
mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
mCblk->waitTimeMs = 0;
mRemainingFrames = mNotificationFramesAct;

View File

@@ -45,6 +45,8 @@ enum {
GET_METADATA,
SUSPEND,
RESUME,
SET_AUX_EFFECT_SEND_LEVEL,
ATTACH_AUX_EFFECT
};
class BpMediaPlayer: public BpInterface<IMediaPlayer>
@@ -221,6 +223,24 @@ public:
return reply.readInt32();
}
status_t setAuxEffectSendLevel(float level)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeFloat(level);
remote()->transact(SET_AUX_EFFECT_SEND_LEVEL, data, &reply);
return reply.readInt32();
}
status_t attachAuxEffect(int effectId)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeInt32(effectId);
remote()->transact(ATTACH_AUX_EFFECT, data, &reply);
return reply.readInt32();
}
};
IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer");
@@ -339,6 +359,16 @@ status_t BnMediaPlayer::onTransact(
reply->setDataPosition(0);
return NO_ERROR;
} break;
case SET_AUX_EFFECT_SEND_LEVEL: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(setAuxEffectSendLevel(data.readFloat()));
return NO_ERROR;
} break;
case ATTACH_AUX_EFFECT: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(attachAuxEffect(data.readInt32()));
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}

View File

@@ -270,6 +270,7 @@ status_t MediaPlayer::start()
MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) {
mPlayer->setLooping(mLoop);
mPlayer->setVolume(mLeftVolume, mRightVolume);
mPlayer->setAuxEffectSendLevel(mSendLevel);
mCurrentState = MEDIA_PLAYER_STARTED;
status_t ret = mPlayer->start();
if (ret != NO_ERROR) {
@@ -523,6 +524,31 @@ int MediaPlayer::getAudioSessionId()
return mAudioSessionId;
}
status_t MediaPlayer::setAuxEffectSendLevel(float level)
{
LOGV("MediaPlayer::setAuxEffectSendLevel(%f)", level);
Mutex::Autolock _l(mLock);
mSendLevel = level;
if (mPlayer != 0) {
return mPlayer->setAuxEffectSendLevel(level);
}
return OK;
}
status_t MediaPlayer::attachAuxEffect(int effectId)
{
LOGV("MediaPlayer::attachAuxEffect(%d)", effectId);
Mutex::Autolock _l(mLock);
if (mPlayer == 0 ||
(mCurrentState & MEDIA_PLAYER_IDLE) ||
(mCurrentState == MEDIA_PLAYER_STATE_ERROR )) {
LOGE("attachAuxEffect called in state %d", mCurrentState);
return INVALID_OPERATION;
}
return mPlayer->attachAuxEffect(effectId);
}
void MediaPlayer::notify(int msg, int ext1, int ext2)
{
LOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);

View File

@@ -329,6 +329,10 @@ status_t MediaPlayerService::AudioOutput::dump(int fd, const Vector<String16>& a
snprintf(buffer, 255, " msec per frame(%f), latency (%d)\n",
mMsecsPerFrame, mLatency);
result.append(buffer);
snprintf(buffer, 255, " aux effect id(%d), send level (%f)\n",
mAuxEffectId, mSendLevel);
result.append(buffer);
::write(fd, result.string(), result.size());
if (mTrack != 0) {
mTrack->dump(fd, args);
@@ -1093,6 +1097,21 @@ status_t MediaPlayerService::Client::setVolume(float leftVolume, float rightVolu
return NO_ERROR;
}
status_t MediaPlayerService::Client::setAuxEffectSendLevel(float level)
{
LOGV("[%d] setAuxEffectSendLevel(%f)", mConnId, level);
Mutex::Autolock l(mLock);
if (mAudioOutput != 0) return mAudioOutput->setAuxEffectSendLevel(level);
return NO_ERROR;
}
status_t MediaPlayerService::Client::attachAuxEffect(int effectId)
{
LOGV("[%d] attachAuxEffect(%d)", mConnId, effectId);
Mutex::Autolock l(mLock);
if (mAudioOutput != 0) return mAudioOutput->attachAuxEffect(effectId);
return NO_ERROR;
}
void MediaPlayerService::Client::notify(void* cookie, int msg, int ext1, int ext2)
{
@@ -1285,6 +1304,8 @@ MediaPlayerService::AudioOutput::AudioOutput(int sessionId)
mRightVolume = 1.0;
mLatency = 0;
mMsecsPerFrame = 0;
mAuxEffectId = 0;
mSendLevel = 0.0;
setMinBufferCount();
}
@@ -1417,10 +1438,13 @@ status_t MediaPlayerService::AudioOutput::open(
LOGV("setVolume");
t->setVolume(mLeftVolume, mRightVolume);
mMsecsPerFrame = 1.e3 / (float) sampleRate;
mLatency = t->latency();
mTrack = t;
return NO_ERROR;
t->setAuxEffectSendLevel(mSendLevel);
return t->attachAuxEffect(mAuxEffectId);;
}
void MediaPlayerService::AudioOutput::start()
@@ -1428,6 +1452,7 @@ void MediaPlayerService::AudioOutput::start()
LOGV("start");
if (mTrack) {
mTrack->setVolume(mLeftVolume, mRightVolume);
mTrack->setAuxEffectSendLevel(mSendLevel);
mTrack->start();
}
}
@@ -1481,6 +1506,26 @@ void MediaPlayerService::AudioOutput::setVolume(float left, float right)
}
}
status_t MediaPlayerService::AudioOutput::setAuxEffectSendLevel(float level)
{
LOGV("setAuxEffectSendLevel(%f)", level);
mSendLevel = level;
if (mTrack) {
return mTrack->setAuxEffectSendLevel(level);
}
return NO_ERROR;
}
status_t MediaPlayerService::AudioOutput::attachAuxEffect(int effectId)
{
LOGV("attachAuxEffect(%d)", effectId);
mAuxEffectId = effectId;
if (mTrack) {
return mTrack->attachAuxEffect(effectId);
}
return NO_ERROR;
}
// static
void MediaPlayerService::AudioOutput::CallbackWrapper(
int event, void *cookie, void *info) {

View File

@@ -91,6 +91,8 @@ class MediaPlayerService : public BnMediaPlayerService
virtual void close();
void setAudioStreamType(int streamType) { mStreamType = streamType; }
void setVolume(float left, float right);
status_t setAuxEffectSendLevel(float level);
status_t attachAuxEffect(int effectId);
virtual status_t dump(int fd, const Vector<String16>& args) const;
static bool isOnEmulator();
@@ -109,7 +111,8 @@ class MediaPlayerService : public BnMediaPlayerService
float mMsecsPerFrame;
uint32_t mLatency;
int mSessionId;
float mSendLevel;
int mAuxEffectId;
static bool mIsOnEmulator;
static int mMinBufferCount; // 12 for emulator; otherwise 4
@@ -221,6 +224,8 @@ private:
Parcel *reply);
virtual status_t suspend();
virtual status_t resume();
virtual status_t setAuxEffectSendLevel(float level);
virtual status_t attachAuxEffect(int effectId);
sp<MediaPlayerBase> createPlayer(player_type playerType);