Merge "Do not draw the fade areas when it's not necessary."
diff --git a/api/current.xml b/api/current.xml
index 1fadcfb..4d86a04 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -19457,6 +19457,19 @@
visibility="public"
>
</constructor>
+<method name="addTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+</method>
<method name="finishContextMode"
return="void"
abstract="true"
@@ -19523,6 +19536,84 @@
visibility="public"
>
</method>
+<method name="insertTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="newTab"
+ return="android.app.ActionBar.Tab"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="removeTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+</method>
+<method name="removeTabAt"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="selectTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+</method>
+<method name="selectTabAt"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
<method name="setBackgroundDrawable"
return="void"
abstract="true"
@@ -19644,6 +19735,30 @@
<parameter name="subtitle" type="java.lang.CharSequence">
</parameter>
</method>
+<method name="setTabNavigationMode"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setTabNavigationMode"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="containerViewId" type="int">
+</parameter>
+</method>
<method name="setTitle"
return="void"
abstract="true"
@@ -19915,6 +20030,128 @@
</parameter>
</method>
</interface>
+<class name="ActionBar.Tab"
+ extends="java.lang.Object"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ActionBar.Tab"
+ type="android.app.ActionBar.Tab"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getFragment"
+ return="android.app.Fragment"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getIcon"
+ return="android.graphics.drawable.Drawable"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPosition"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getText"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="select"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setFragment"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+</method>
+<method name="setIcon"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="icon" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+<method name="setText"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
+<field name="INVALID_POSITION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<class name="Activity"
extends="android.view.ContextThemeWrapper"
abstract="false"
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 67133e0..d0b3ac4 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -206,9 +206,11 @@
*
* @return The current navigation mode.
*
+ * @see #setStandardNavigationMode()
* @see #setStandardNavigationMode(CharSequence)
* @see #setStandardNavigationMode(CharSequence, CharSequence)
* @see #setDropdownNavigationMode(SpinnerAdapter)
+ * @see #setTabNavigationMode()
* @see #setCustomNavigationMode(View)
*/
public abstract int getNavigationMode();
@@ -217,10 +219,98 @@
* @return The current set of display options.
*/
public abstract int getDisplayOptions();
-
+
+ /**
+ * Start a context mode controlled by <code>callback</code>.
+ * The {@link ContextModeCallback} will receive lifecycle events for the duration
+ * of the context mode.
+ *
+ * @param callback Callback handler that will manage this context mode.
+ */
public abstract void startContextMode(ContextModeCallback callback);
+
+ /**
+ * Finish the current context mode.
+ */
public abstract void finishContextMode();
-
+
+ /**
+ * Set the action bar into tabbed navigation mode.
+ *
+ * @see #addTab(Tab)
+ * @see #insertTab(Tab, int)
+ * @see #removeTab(Tab)
+ * @see #removeTabAt(int)
+ */
+ public abstract void setTabNavigationMode();
+
+ /**
+ * Set the action bar into tabbed navigation mode.
+ *
+ * @param containerViewId Id of the container view where tab content fragments should appear.
+ *
+ * @see #addTab(Tab)
+ * @see #insertTab(Tab, int)
+ * @see #removeTab(Tab)
+ * @see #removeTabAt(int)
+ */
+ public abstract void setTabNavigationMode(int containerViewId);
+
+ /**
+ * Create and return a new {@link Tab}.
+ * This tab will not be included in the action bar until it is added.
+ *
+ * @return A new Tab
+ *
+ * @see #addTab(Tab)
+ * @see #insertTab(Tab, int)
+ */
+ public abstract Tab newTab();
+
+ /**
+ * Add a tab for use in tabbed navigation mode. The tab will be added at the end of the list.
+ *
+ * @param tab Tab to add
+ */
+ public abstract void addTab(Tab tab);
+
+ /**
+ * Insert a tab for use in tabbed navigation mode. The tab will be inserted at
+ * <code>position</code>.
+ *
+ * @param tab The tab to add
+ * @param position The new position of the tab
+ */
+ public abstract void insertTab(Tab tab, int position);
+
+ /**
+ * Remove a tab from the action bar.
+ *
+ * @param tab The tab to remove
+ */
+ public abstract void removeTab(Tab tab);
+
+ /**
+ * Remove a tab from the action bar.
+ *
+ * @param position Position of the tab to remove
+ */
+ public abstract void removeTabAt(int position);
+
+ /**
+ * Select the specified tab. If it is not a child of this action bar it will be added.
+ *
+ * @param tab Tab to select
+ */
+ public abstract void selectTab(Tab tab);
+
+ /**
+ * Select the tab at <code>position</code>
+ *
+ * @param position Position of the tab to select
+ */
+ public abstract void selectTabAt(int position);
+
/**
* Represents a contextual mode of the Action Bar. Context modes can be used for
* modal interactions with activity content and replace the normal Action Bar until finished.
@@ -350,4 +440,74 @@
*/
public boolean onNavigationItemSelected(int itemPosition, long itemId);
}
+
+ /**
+ * A tab in the action bar.
+ *
+ * <p>Tabs manage the hiding and showing of {@link Fragment}s.
+ */
+ public static abstract class Tab {
+ /**
+ * An invalid position for a tab.
+ *
+ * @see #getPosition()
+ */
+ public static final int INVALID_POSITION = -1;
+
+ /**
+ * Return the current position of this tab in the action bar.
+ *
+ * @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in
+ * the action bar.
+ */
+ public abstract int getPosition();
+
+ /**
+ * Return the icon associated with this tab.
+ *
+ * @return The tab's icon
+ */
+ public abstract Drawable getIcon();
+
+ /**
+ * Return the text of this tab.
+ *
+ * @return The tab's text
+ */
+ public abstract CharSequence getText();
+
+ /**
+ * Set the icon displayed on this tab.
+ *
+ * @param icon The drawable to use as an icon
+ */
+ public abstract void setIcon(Drawable icon);
+
+ /**
+ * Set the text displayed on this tab. Text may be truncated if there is not
+ * room to display the entire string.
+ *
+ * @param text The text to display
+ */
+ public abstract void setText(CharSequence text);
+
+ /**
+ * Returns the fragment that will be shown when this tab is selected.
+ *
+ * @return Fragment associated with this tab
+ */
+ public abstract Fragment getFragment();
+
+ /**
+ * Set the fragment that will be shown when this tab is selected.
+ *
+ * @param fragment Fragment to associate with this tab
+ */
+ public abstract void setFragment(Fragment fragment);
+
+ /**
+ * Select this tab. Only valid if the tab has been added to the action bar.
+ */
+ public abstract void select();
+ }
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 8825b8c..4166b89 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1676,11 +1676,11 @@
*/
private void initActionBar() {
Window window = getWindow();
- if (!window.hasFeature(Window.FEATURE_ACTION_BAR)) {
+ if (!window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
return;
}
- mActionBar = new ActionBarImpl(getWindow().getDecorView());
+ mActionBar = new ActionBarImpl(this);
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 3c5a029..441370a 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -613,7 +613,10 @@
// This thread didn't already have the lock, so begin a database
// transaction now.
- if (exclusive) {
+ // STOPSHIP - uncomment the following 1 line
+ // if (exclusive) {
+ // STOPSHIP - remove the following 1 line
+ if (exclusive && mConnectionPool == null) {
execSQL("BEGIN EXCLUSIVE;");
} else {
execSQL("BEGIN IMMEDIATE;");
@@ -946,7 +949,10 @@
sBlockSize = new StatFs("/data").getBlockSize();
}
sqliteDatabase.setPageSize(sBlockSize);
- sqliteDatabase.setJournalMode(path, "TRUNCATE");
+ //STOPSHIP - uncomment the following line
+ //sqliteDatabase.setJournalMode(path, "TRUNCATE");
+ // STOPSHIP remove the following lines
+ sqliteDatabase.enableWriteAheadLogging();
// add this database to the list of databases opened in this process
ActiveDatabases.addActiveDatabase(sqliteDatabase);
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 0a2899f..1100886 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -535,6 +535,7 @@
* application does not need a particular callback, a null can be passed
* instead of a callback method.
*
+ * This method is only valid after {@link #startPreview()} has been called.
* This method will stop the preview. Applications should not call {@link
* #stopPreview()} before this. After jpeg callback is received,
* applications can call {@link #startPreview()} to restart the preview.
@@ -562,6 +563,7 @@
* application does not need a particular callback, a null can be passed
* instead of a callback method.
*
+ * This method is only valid after {@link #startPreview()} has been called.
* This method will stop the preview. Applications should not call {@link
* #stopPreview()} before this. After jpeg callback is received,
* applications can call {@link #startPreview()} to restart the preview.
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 74acb98..a23a5a7 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -78,9 +78,26 @@
/**
+ * Generic columns for use by sync adapters. The specific functions of
+ * these columns are private to the sync adapter. Other clients of the API
+ * should not attempt to either read or write this column.
+ */
+ protected interface BaseSyncColumns {
+
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC1 = "sync1";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC2 = "sync2";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC3 = "sync3";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC4 = "sync4";
+ }
+
+ /**
* Columns for Sync information used by Calendars and Events tables.
*/
- public interface SyncColumns {
+ public interface SyncColumns extends BaseSyncColumns {
/**
* The account that was used to sync the entry to the device.
* <P>Type: TEXT</P>
@@ -269,11 +286,9 @@
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _SYNC_DIRTY);
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _SYNC_MARK);
- DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SELF_URL);
- DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
- Calendars.EDIT_URL);
- DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
- Calendars.EVENTS_URL);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC2);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC3);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.NAME);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
@@ -848,8 +863,7 @@
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _SYNC_DIRTY);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED);
- DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
- Calendars.EVENTS_URL);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
Entity entity = new Entity(cv);
Cursor subCursor;
diff --git a/core/java/android/webruntime/WebRuntimeActivity.java b/core/java/android/webruntime/WebRuntimeActivity.java
index 07d9908..ec8c60c 100644
--- a/core/java/android/webruntime/WebRuntimeActivity.java
+++ b/core/java/android/webruntime/WebRuntimeActivity.java
@@ -141,7 +141,7 @@
@Override
public void onPageFinished(WebView view, String url) {
- if (mSplashScreen.getVisibility() == View.VISIBLE) {
+ if (mSplashScreen != null && mSplashScreen.getVisibility() == View.VISIBLE) {
mSplashScreen.setVisibility(View.GONE);
mSplashScreen = null;
}
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 6ac68aa..6cf455c 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -22,6 +22,9 @@
import com.android.internal.widget.ActionBarView;
import android.app.ActionBar;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.Menu;
@@ -31,6 +34,8 @@
import android.widget.SpinnerAdapter;
import android.widget.ViewAnimator;
+import java.util.ArrayList;
+
/**
* ActionBarImpl is the ActionBar implementation used
* by devices of all screen sizes. If it detects a compatible decor,
@@ -42,10 +47,21 @@
private static final int NORMAL_VIEW = 0;
private static final int CONTEXT_VIEW = 1;
+ private static final int TAB_SWITCH_SHOW_HIDE = 0;
+ private static final int TAB_SWITCH_ADD_REMOVE = 1;
+
+ private Activity mActivity;
+
private ViewAnimator mAnimatorView;
private ActionBarView mActionView;
private ActionBarContextView mUpperContextView;
private LinearLayout mLowerContextView;
+
+ private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
+
+ private int mTabContainerViewId = android.R.id.content;
+ private TabImpl mSelectedTab;
+ private int mTabSwitchMode = TAB_SWITCH_ADD_REMOVE;
private ContextMode mContextMode;
@@ -64,7 +80,9 @@
}
};
- public ActionBarImpl(View decor) {
+ public ActionBarImpl(Activity activity) {
+ final View decor = activity.getWindow().getDecorView();
+ mActivity = activity;
mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
mUpperContextView = (ActionBarContextView) decor.findViewById(
com.android.internal.R.id.action_context_bar);
@@ -83,32 +101,54 @@
}
public void setCustomNavigationMode(View view) {
+ cleanupTabs();
mActionView.setCustomNavigationView(view);
mActionView.setCallback(null);
}
public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
+ cleanupTabs();
mActionView.setCallback(callback);
mActionView.setNavigationMode(NAVIGATION_MODE_DROPDOWN_LIST);
mActionView.setDropdownAdapter(adapter);
}
public void setStandardNavigationMode() {
+ cleanupTabs();
mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
mActionView.setCallback(null);
}
public void setStandardNavigationMode(CharSequence title) {
+ cleanupTabs();
setStandardNavigationMode(title, null);
}
public void setStandardNavigationMode(CharSequence title, CharSequence subtitle) {
+ cleanupTabs();
mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
mActionView.setTitle(title);
mActionView.setSubtitle(subtitle);
mActionView.setCallback(null);
}
+ private void cleanupTabs() {
+ if (mSelectedTab != null) {
+ selectTab(null);
+ }
+ if (!mTabs.isEmpty()) {
+ if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
+ final FragmentTransaction trans = mActivity.openFragmentTransaction();
+ final int tabCount = mTabs.size();
+ for (int i = 0; i < tabCount; i++) {
+ trans.remove(mTabs.get(i).getFragment());
+ }
+ trans.commit();
+ }
+ mTabs.clear();
+ }
+ }
+
public void setTitle(CharSequence title) {
mActionView.setTitle(title);
}
@@ -174,6 +214,113 @@
}
}
+ private void configureTab(Tab tab, int position) {
+ final TabImpl tabi = (TabImpl) tab;
+ final boolean isFirstTab = mTabs.isEmpty();
+ final FragmentTransaction trans = mActivity.openFragmentTransaction();
+ final Fragment frag = tabi.getFragment();
+
+ tabi.setPosition(position);
+ mTabs.add(position, tabi);
+
+ if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
+ if (!frag.isAdded()) {
+ trans.add(mTabContainerViewId, frag);
+ }
+ }
+
+ if (isFirstTab) {
+ if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
+ trans.show(frag);
+ } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
+ trans.add(mTabContainerViewId, frag);
+ }
+ mSelectedTab = tabi;
+ } else {
+ if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
+ trans.hide(frag);
+ }
+ }
+ trans.commit();
+ }
+
+ @Override
+ public void addTab(Tab tab) {
+ mActionView.addTab(tab);
+ configureTab(tab, mTabs.size());
+ }
+
+ @Override
+ public void insertTab(Tab tab, int position) {
+ mActionView.insertTab(tab, position);
+ configureTab(tab, position);
+ }
+
+ @Override
+ public Tab newTab() {
+ return new TabImpl();
+ }
+
+ @Override
+ public void removeTab(Tab tab) {
+ removeTabAt(tab.getPosition());
+ }
+
+ @Override
+ public void removeTabAt(int position) {
+ mActionView.removeTabAt(position);
+ mTabs.remove(position);
+
+ final int newTabCount = mTabs.size();
+ for (int i = position; i < newTabCount; i++) {
+ mTabs.get(i).setPosition(i);
+ }
+
+ selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
+ }
+
+ @Override
+ public void setTabNavigationMode() {
+ mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
+ }
+
+ @Override
+ public void setTabNavigationMode(int containerViewId) {
+ mTabContainerViewId = containerViewId;
+ setTabNavigationMode();
+ }
+
+ @Override
+ public void selectTab(Tab tab) {
+ if (mSelectedTab == tab) {
+ return;
+ }
+
+ mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
+ final FragmentTransaction trans = mActivity.openFragmentTransaction();
+ if (mSelectedTab != null) {
+ if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
+ trans.hide(mSelectedTab.getFragment());
+ } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
+ trans.remove(mSelectedTab.getFragment());
+ }
+ }
+ if (tab != null) {
+ if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
+ trans.show(tab.getFragment());
+ } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
+ trans.add(mTabContainerViewId, tab.getFragment());
+ }
+ }
+ mSelectedTab = (TabImpl) tab;
+ trans.commit();
+ }
+
+ @Override
+ public void selectTabAt(int position) {
+ selectTab(mTabs.get(position));
+ }
+
/**
* @hide
*/
@@ -233,6 +380,60 @@
if (!actionItem.invoke()) {
mCallback.onContextItemClicked(this, item);
}
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public class TabImpl extends ActionBar.Tab {
+ private Fragment mFragment;
+ private Drawable mIcon;
+ private CharSequence mText;
+ private int mPosition;
+
+ @Override
+ public Fragment getFragment() {
+ return mFragment;
+ }
+
+ @Override
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ @Override
+ public int getPosition() {
+ return mPosition;
+ }
+
+ public void setPosition(int position) {
+ mPosition = position;
+ }
+
+ @Override
+ public CharSequence getText() {
+ return mText;
+ }
+
+ @Override
+ public void setFragment(Fragment fragment) {
+ mFragment = fragment;
+ }
+
+ @Override
+ public void setIcon(Drawable icon) {
+ mIcon = icon;
+ }
+
+ @Override
+ public void setText(CharSequence text) {
+ mText = text;
+ }
+
+ @Override
+ public void select() {
+ selectTab(this);
}
}
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index e919f1b..48707b9 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -31,8 +31,10 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
+import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
@@ -85,6 +87,7 @@
private TextView mTitleView;
private TextView mSubtitleView;
private Spinner mSpinner;
+ private LinearLayout mTabLayout;
private View mCustomNavView;
private boolean mShowMenu;
@@ -128,7 +131,8 @@
ApplicationInfo info = context.getApplicationInfo();
PackageManager pm = context.getPackageManager();
- mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode, ActionBar.NAVIGATION_MODE_STANDARD);
+ mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode,
+ ActionBar.NAVIGATION_MODE_STANDARD);
mTitle = a.getText(R.styleable.ActionBar_title);
mSubtitle = a.getText(R.styleable.ActionBar_subtitle);
mDisplayOptions = a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT);
@@ -271,6 +275,11 @@
mCustomNavView = null;
}
break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ if (mTabLayout != null) {
+ removeView(mTabLayout);
+ mTabLayout = null;
+ }
}
switch (mode) {
@@ -286,6 +295,10 @@
case ActionBar.NAVIGATION_MODE_CUSTOM:
addView(mCustomNavView);
break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ mTabLayout = new LinearLayout(getContext());
+ addView(mTabLayout);
+ break;
}
mNavigationMode = mode;
requestLayout();
@@ -308,6 +321,35 @@
return mDisplayOptions;
}
+ private TabView createTabView(ActionBar.Tab tab) {
+ final TabView tabView = new TabView(getContext(), tab);
+ tabView.setFocusable(true);
+ tabView.setOnClickListener(new TabClickListener());
+ return tabView;
+ }
+
+ public void addTab(ActionBar.Tab tab) {
+ final boolean isFirst = mTabLayout.getChildCount() == 0;
+ final TabView tabView = createTabView(tab);
+ mTabLayout.addView(tabView);
+ if (isFirst) {
+ tabView.setSelected(true);
+ }
+ }
+
+ public void insertTab(ActionBar.Tab tab, int position) {
+ final boolean isFirst = mTabLayout.getChildCount() == 0;
+ final TabView tabView = createTabView(tab);
+ mTabLayout.addView(tabView, position);
+ if (isFirst) {
+ tabView.setSelected(true);
+ }
+ }
+
+ public void removeTabAt(int position) {
+ mTabLayout.removeViewAt(position);
+ }
+
@Override
protected LayoutParams generateDefaultLayoutParams() {
// Used by custom nav views if they don't supply layout params. Everything else
@@ -356,7 +398,7 @@
case ActionBar.NAVIGATION_MODE_TABS:
throw new UnsupportedOperationException(
- "Tab navigation isn't supported yet!");
+ "Inflating tab navigation isn't supported yet!");
case ActionBar.NAVIGATION_MODE_CUSTOM:
if (mCustomNavView != null) {
@@ -381,6 +423,14 @@
addView(mTitleLayout);
}
+ public void setTabSelected(int position) {
+ final int tabCount = mTabLayout.getChildCount();
+ for (int i = 0; i < tabCount; i++) {
+ final View child = mTabLayout.getChildAt(i);
+ child.setSelected(i == position);
+ }
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
@@ -442,6 +492,13 @@
MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode));
}
break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ if (mTabLayout != null) {
+ mTabLayout.measure(
+ MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ }
+ break;
}
setMeasuredDimension(contentWidth, mContentHeight);
@@ -486,6 +543,10 @@
x += positionChild(mCustomNavView, x, y, contentHeight) + mSpacing;
}
break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ if (mTabLayout != null) {
+ x += positionChild(mTabLayout, x, y, contentHeight) + mSpacing;
+ }
}
x = r - l - getPaddingRight();
@@ -515,4 +576,59 @@
return childWidth;
}
+
+ private static class TabView extends LinearLayout {
+ private ActionBar.Tab mTab;
+
+ public TabView(Context context, ActionBar.Tab tab) {
+ super(context);
+ mTab = tab;
+
+ // TODO Style tabs based on the theme
+
+ final Drawable icon = tab.getIcon();
+ final CharSequence text = tab.getText();
+
+ if (icon != null) {
+ ImageView iconView = new ImageView(context);
+ iconView.setImageDrawable(icon);
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ iconView.setLayoutParams(lp);
+ addView(iconView);
+ }
+
+ if (text != null) {
+ TextView textView = new TextView(context);
+ textView.setText(text);
+ textView.setSingleLine();
+ textView.setEllipsize(TruncateAt.END);
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ textView.setLayoutParams(lp);
+ addView(textView);
+ }
+
+ setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT, 1));
+ }
+
+ public ActionBar.Tab getTab() {
+ return mTab;
+ }
+ }
+
+ private class TabClickListener implements OnClickListener {
+ public void onClick(View view) {
+ TabView tabView = (TabView) view;
+ tabView.getTab().select();
+ final int tabCount = mTabLayout.getChildCount();
+ for (int i = 0; i < tabCount; i++) {
+ final View child = mTabLayout.getChildAt(i);
+ child.setSelected(child == view);
+ }
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index 8006b73..ca8dac1 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -199,7 +199,7 @@
private boolean inXact = false;
private int numXacts;
- private static final int TIME_TO_RUN_WAL_TEST_FOR = 15; // num sec this test shoudl run
+ private static final int TIME_TO_RUN_WAL_TEST_FOR = 15; // num sec this test should run
private int[][] counts = new int[2][2];
private synchronized boolean inXact() {
@@ -334,7 +334,7 @@
/**
* test to make sure the statement finalizations are not done right away but
- * piggybacked onto the next sql statement execution on the same database.
+ * piggy-backed onto the next sql statement execution on the same database.
*/
@SmallTest
public void testStatementClose() {
@@ -356,7 +356,7 @@
}
// add one more to the cache - and the above 'stmt0Id' should fall out of cache
- SQLiteStatement stmt1 = mDatabase.compileStatement("select * from test where i = ?;");
+ SQLiteStatement stmt1 = mDatabase.compileStatement("insert into test values(100, ?);");
stmt1.bindLong(1, 1);
stmt1.close();
@@ -372,7 +372,7 @@
/**
* same as above - except that the statement to be finalized is from Thread # 1.
- * and it is eventually finalized in Thread # 2 when it executes a sql statement.
+ * and it is eventually finalized in Thread # 2 when it executes a SQL statement.
* @throws InterruptedException
*/
@LargeTest
@@ -405,7 +405,7 @@
Thread t2 = new Thread() {
@Override public void run() {
SQLiteStatement stmt1 = mDatabase.compileStatement(
- "select * from test where i = ?;");
+ "insert into test values(100, ?);");
stmt1.bindLong(1, 1);
stmt1.close();
}
@@ -476,7 +476,7 @@
Thread t2 = new Thread() {
@Override public void run() {
SQLiteStatement stmt1 = mDatabase.compileStatement(
- "select * from test where i = ?;");
+ "insert into test values(100, ?);");
stmt1.bindLong(1, 1);
stmt1.close();
}
diff --git a/graphics/java/android/renderscript/Script.java b/graphics/java/android/renderscript/Script.java
index beac997..d9aec59 100644
--- a/graphics/java/android/renderscript/Script.java
+++ b/graphics/java/android/renderscript/Script.java
@@ -77,6 +77,10 @@
mRS.nScriptSetVarI(mID, index, v);
}
+ public void setVar(int index, boolean v) {
+ mRS.nScriptSetVarI(mID, index, v ? 1 : 0);
+ }
+
public void setVar(int index, FieldPacker v) {
mRS.nScriptSetVarV(mID, index, v.getData());
}
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 @@
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 @@
* - 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 @@
* - 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 @@
* 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 @@
* - 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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{
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 @@
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 @@
}
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::reset()
+void AudioFlinger::EffectModule::reset_l()
{
if (mEffectInterface == NULL) {
return;
@@ -5205,6 +5221,7 @@
status_t AudioFlinger::EffectModule::init()
{
+ Mutex::Autolock _l(mLock);
if (mEffectInterface == NULL) {
return NO_INIT;
}
@@ -5217,7 +5234,7 @@
return status;
}
-status_t AudioFlinger::EffectModule::start()
+status_t AudioFlinger::EffectModule::start_l()
{
if (mEffectInterface == NULL) {
return NO_INIT;
@@ -5231,7 +5248,7 @@
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::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 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 @@
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::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::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 @@
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 @@
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 @@
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/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 882ad83..bcda45e7 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -18,6 +18,8 @@
#include <GLES2/gl2.h>
+#include <utils/Log.h>
+
#include "LayerCache.h"
namespace android {
@@ -84,11 +86,55 @@
mCache.setOnEntryRemovedListener(NULL);
}
-Layer* LayerCache::get(LayerSize& size) {
+Layer* LayerCache::get(LayerSize& size, GLuint previousFbo) {
Layer* layer = mCache.remove(size);
if (layer) {
+ LAYER_LOGD("Reusing layer");
+
mSize -= layer->layer.getWidth() * layer->layer.getHeight() * 4;
+ } else {
+ LAYER_LOGD("Creating new layer");
+
+ layer = new Layer;
+ layer->blend = true;
+
+ // Generate the FBO and attach the texture
+ glGenFramebuffers(1, &layer->fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+
+ // Generate the texture in which the FBO will draw
+ glGenTextures(1, &layer->texture);
+ glBindTexture(GL_TEXTURE_2D, layer->texture);
+
+ // The FBO will not be scaled, so we can use lower quality filtering
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width, size.height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // Bind texture to FBO
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ layer->texture, 0);
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ LOGE("Framebuffer incomplete (GL error code 0x%x)", status);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+
+ glDeleteFramebuffers(1, &layer->fbo);
+ glDeleteTextures(1, &layer->texture);
+ delete layer;
+
+ return NULL;
+ }
}
+
return layer;
}
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 519552d..adc6713 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -23,6 +23,24 @@
namespace android {
namespace uirenderer {
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_LAYERS 0
+
+// Debug
+#if DEBUG_LAYERS
+ #define LAYER_LOGD(...) LOGD(__VA_ARGS__)
+#else
+ #define LAYER_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache
+///////////////////////////////////////////////////////////////////////////////
+
class LayerCache: public OnEntryRemoved<LayerSize, Layer*> {
public:
LayerCache(uint32_t maxByteSize);
@@ -35,13 +53,25 @@
void operator()(LayerSize& bitmap, Layer*& texture);
/**
- * Returns the layer of specified dimensions, NULL if cannot be found.
+ * Returns the layer of specified dimensions. If not suitable layer
+ * can be found, a new one is created and returned. If creating a new
+ * layer fails, NULL is returned.
+ *
+ * When a layer is obtained from the cache, it is removed and the total
+ * size of the cache goes down.
+ *
+ * @param size The dimensions of the desired layer
+ * @param previousFbo The name of the FBO to bind to if creating a new
+ * layer fails
*/
- Layer* get(LayerSize& size);
+ Layer* get(LayerSize& size, GLuint previousFbo);
/**
* Adds the layer to the cache. The layer will not be added if there is
* not enough space available.
*
+ * @param size The dimensions of the layer
+ * @param layer The layer to add to the cache
+ *
* @return True if the layer was added, false otherwise.
*/
bool put(LayerSize& size, Layer* layer);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index ae7719c..027ed79 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -34,9 +34,6 @@
// Defines
///////////////////////////////////////////////////////////////////////////////
-// Debug
-#define DEBUG_LAYERS 0
-
// These properties are defined in mega-bytes
#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
#define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size"
@@ -51,13 +48,6 @@
#define SV(x, y) { { x, y } }
#define FV(x, y, u, v) { { x, y }, { u, v } }
-// Debug
-#if DEBUG_LAYERS
- #define LAYER_LOGD(...) LOGD(__VA_ARGS__)
-#else
- #define LAYER_LOGD(...)
-#endif
-
///////////////////////////////////////////////////////////////////////////////
// Globals
///////////////////////////////////////////////////////////////////////////////
@@ -253,6 +243,8 @@
layer->texture, layer->alpha, layer->mode, layer->blend, true);
LayerSize size(rect.getWidth(), rect.getHeight());
+ // Failing to add the layer to the cache should happen only if the
+ // layer is too large
if (!mLayerCache.put(size, layer)) {
LAYER_LOGD("Deleting layer");
@@ -300,63 +292,20 @@
bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) {
- LayerSize size(right - left, bottom - top);
- Layer* layer = mLayerCache.get(size);
-
LAYER_LOGD("Requesting layer %dx%d", size.width, size.height);
LAYER_LOGD("Layer cache size = %d", mLayerCache.getSize());
+ GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
+ LayerSize size(right - left, bottom - top);
+
+ // TODO VERY IMPORTANT: Fix TextView to not call saveLayer() all the time
+ Layer* layer = mLayerCache.get(size, previousFbo);
if (!layer) {
- LAYER_LOGD("Creating new layer");
-
- layer = new Layer;
- layer->blend = true;
-
- // Generate the FBO and attach the texture
- glGenFramebuffers(1, &layer->fbo);
- glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
-
- // Generate the texture in which the FBO will draw
- glGenTextures(1, &layer->texture);
- glBindTexture(GL_TEXTURE_2D, layer->texture);
-
- // The FBO will not be scaled, so we can use lower quality filtering
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- // TODO VERY IMPORTANT: Fix TextView to not call saveLayer() all the time
-
- const GLsizei width = right - left;
- const GLsizei height = bottom - top;
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- // Bind texture to FBO
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- layer->texture, 0);
-
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- LOGD("Framebuffer incomplete (GL error code 0x%x)", status);
-
- GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
- glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
-
- glDeleteFramebuffers(1, &layer->fbo);
- glDeleteTextures(1, &layer->texture);
- delete layer;
-
- return false;
- }
- } else {
- LAYER_LOGD("Reusing layer");
- glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+ return false;
}
+ glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+
// Clear the FBO
glDisable(GL_SCISSOR_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
diff --git a/libs/rs/java/ImageProcessing/res/drawable/data.jpg b/libs/rs/java/ImageProcessing/res/drawable/data.jpg
new file mode 100644
index 0000000..81a87b1
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/res/drawable/data.jpg
Binary files differ
diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh
index 8bf53bf..3709296 100644
--- a/libs/rs/scriptc/rs_math.rsh
+++ b/libs/rs/scriptc/rs_math.rsh
@@ -1,3 +1,6 @@
+#ifndef __RS_MATH_RSH__
+#define __RS_MATH_RSH__
+
#include "rs_cl.rsh"
#include "rs_core.rsh"
@@ -52,4 +55,4 @@
extern void __attribute__((overloadable))rsForEach(rs_script, rs_allocation input, int xStart, int yStart, int xEnd, int yEnd);
extern void __attribute__((overloadable))rsForEach(rs_script, rs_allocation input, rs_allocation output, int xStart, int yStart, int xEnd, int yEnd);
-
+#endif
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.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 @@
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");
/**
- * State of an AudioEffect object that was not successfully initialized upon creation
+ * 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");
+
+ /**
+ * 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 @@
*/
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;
- // 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:<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() {
+ }
- /**
- * 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(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 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 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";
- 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 @@
/**
* 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 @@
*/
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 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 @@
}
/**
- * 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 @@
/**
* 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.
+ * 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 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.
- * @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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
/**
* 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
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 @@
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 @@
}
/**
- * 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 @@
/**
* Sets the listener AudioEffect notifies when a parameter is changed.
+ *
* @param listener
*/
public void setParameterListener(OnParameterChangeListener listener) {
@@ -691,7 +781,7 @@
// 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 @@
}
}
- //---------------------------------------------------------
+ // ---------------------------------------------------------
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
return byteArrayToInt(valueBuf, 0);
}
+
protected int byteArrayToInt(byte[] valueBuf, int offset) {
ByteBuffer converter = ByteBuffer.wrap(valueBuf);
converter.order(ByteOrder.nativeOrder());
@@ -931,12 +1024,12 @@
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 ca88bcc..47f1974 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -681,18 +681,6 @@
}
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 @@
{"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 @@
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 @@
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 @@
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 @@
return false;
}
- return (jboolean)lpAudioEffect->isEnabled();
+ return (jboolean)lpAudioEffect->getEnabled();
}
@@ -516,7 +497,7 @@
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 @@
(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 @@
// ----------------------------------------------------------------------------
+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 @@
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 @@
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 @@
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 @@
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 @@
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();
+ 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;
-}
-
-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();
- }
-
return INVALID_OPERATION;
}
@@ -349,7 +341,7 @@
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 @@
DECODE_FD,
CREATE_MEDIA_RECORDER,
CREATE_METADATA_RETRIEVER,
- GET_OMX,
- SNOOP
+ GET_OMX
};
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
@@ -134,14 +133,6 @@
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 @@
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 @@
}
-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 @@
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 @@
mRightVolume = 1.0;
mLatency = 0;
mMsecsPerFrame = 0;
- mNumFramesWritten = 0;
setMinBufferCount();
}
@@ -1516,30 +1423,9 @@
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 @@
//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 @@
{
LOGV("stop");
if (mTrack) mTrack->stop();
- lastWriteTime = 0;
}
void MediaPlayerService::AudioOutput::flush()
@@ -1573,7 +1456,6 @@
{
LOGV("pause");
if (mTrack) mTrack->pause();
- lastWriteTime = 0;
}
void MediaPlayerService::AudioOutput::close()
@@ -1609,9 +1491,6 @@
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 @@
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 @@
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);