diff options
38 files changed, 3356 insertions, 712 deletions
diff --git a/api/current.xml b/api/current.xml index 1fadcfb1f0f7..4d86a04c79fd 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 67133e0fb489..d0b3ac455411 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -206,9 +206,11 @@ public abstract class ActionBar { * * @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 @@ public abstract class ActionBar { * @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 abstract class ActionBar { */ 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 8825b8cc7587..4166b897e836 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1676,11 +1676,11 @@ public class Activity extends ContextThemeWrapper */ 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 3c5a0290f3dc..441370ae0bf1 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -613,7 +613,10 @@ public class SQLiteDatabase extends SQLiteClosable { // 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 @@ public class SQLiteDatabase extends SQLiteClosable { 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 0a2899fefeba..11008862edaf 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -535,6 +535,7 @@ public class Camera { * 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 @@ public class Camera { * 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 74acb9861d5f..a23a5a746575 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -78,9 +78,26 @@ public final class Calendar { /** + * 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 @@ public final class Calendar { 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 @@ public final class Calendar { 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 07d9908c94cf..ec8c60c6d1dc 100644 --- a/core/java/android/webruntime/WebRuntimeActivity.java +++ b/core/java/android/webruntime/WebRuntimeActivity.java @@ -141,7 +141,7 @@ public class WebRuntimeActivity extends Activity @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 6ac68aa3b545..6cf455c341bf 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.ActionBarContextView; 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.LinearLayout; 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 @@ public class ActionBarImpl extends ActionBar { 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 class ActionBarImpl extends ActionBar { } }; - 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 class ActionBarImpl extends ActionBar { } 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 @@ public class ActionBarImpl extends ActionBar { } } + 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 @@ public class ActionBarImpl extends ActionBar { 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 e919f1b39528..48707b959548 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.content.res.TypedArray; 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 @@ public class ActionBarView extends ViewGroup { private TextView mTitleView; private TextView mSubtitleView; private Spinner mSpinner; + private LinearLayout mTabLayout; private View mCustomNavView; private boolean mShowMenu; @@ -128,7 +131,8 @@ public class ActionBarView extends ViewGroup { 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 @@ public class ActionBarView extends ViewGroup { mCustomNavView = null; } break; + case ActionBar.NAVIGATION_MODE_TABS: + if (mTabLayout != null) { + removeView(mTabLayout); + mTabLayout = null; + } } switch (mode) { @@ -286,6 +295,10 @@ public class ActionBarView extends ViewGroup { 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 @@ public class ActionBarView extends ViewGroup { 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 @@ public class ActionBarView extends ViewGroup { 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 @@ public class ActionBarView extends ViewGroup { 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 @@ public class ActionBarView extends ViewGroup { 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 @@ public class ActionBarView extends ViewGroup { 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 @@ public class ActionBarView extends ViewGroup { 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 8006b738b1ca..ca8dac13efd6 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 @@ public class SQLiteDatabaseTest extends AndroidTestCase { 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 @@ public class SQLiteDatabaseTest extends AndroidTestCase { /** * 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 @@ public class SQLiteDatabaseTest extends AndroidTestCase { } // 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 @@ public class SQLiteDatabaseTest extends AndroidTestCase { /** * 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 @@ public class SQLiteDatabaseTest extends AndroidTestCase { 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 @@ public class SQLiteDatabaseTest extends AndroidTestCase { 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 beac99713369..d9aec593c553 100644 --- a/graphics/java/android/renderscript/Script.java +++ b/graphics/java/android/renderscript/Script.java @@ -77,6 +77,10 @@ public class Script extends BaseObj { 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 66670f3942d3..e9ff8a35e7df 100644 --- a/include/media/AudioEffect.h +++ b/include/media/AudioEffect.h @@ -307,29 +307,18 @@ public: int32_t priority() const { return mPriority; } - /* Enables the effect engine. + /* Enables or disables the effect engine. * * Parameters: - * None. + * enabled: requested enable state. * * Returned status (from utils/Errors.h) can be: * - NO_ERROR: successful operation - * - INVALID_OPERATION: the application does not have control of the effect engine + * - INVALID_OPERATION: the application does not have control of the effect engine or the + * effect is already in the requested state. */ - status_t enable(); - - /* Disables the effect engine. - * - * Parameters: - * None. - * - * Returned status (from utils/Errors.h) can be: - * - NO_ERROR: successful operation - * - INVALID_OPERATION: the application does not have control of the effect engine - */ - status_t disable(); - - bool isEnabled() const; + virtual status_t setEnabled(bool enabled); + bool getEnabled() const; /* Sets a parameter value. * @@ -342,7 +331,7 @@ public: * - BAD_VALUE: invalid parameter identifier or value. * - DEAD_OBJECT: the effect engine has been deleted. */ - status_t setParameter(effect_param_t *param); + virtual status_t setParameter(effect_param_t *param); /* Prepare a new parameter value that will be set by next call to * setParameterCommit(). This method can be used to set multiple parameters @@ -359,7 +348,7 @@ public: * - NO_MEMORY: no more space available in shared memory used for deferred parameter * setting. */ - status_t setParameterDeferred(effect_param_t *param); + virtual status_t setParameterDeferred(effect_param_t *param); /* Commit all parameter values previously prepared by setParameterDeferred(). * @@ -373,7 +362,7 @@ public: * as to which of the parameters caused this error. * - DEAD_OBJECT: the effect engine has been deleted. */ - status_t setParameterCommit(); + virtual status_t setParameterCommit(); /* Gets a parameter value. * @@ -387,13 +376,17 @@ public: * - BAD_VALUE: invalid parameter identifier. * - DEAD_OBJECT: the effect engine has been deleted. */ - status_t getParameter(effect_param_t *param); + virtual status_t getParameter(effect_param_t *param); /* Sends a command and receives a response to/from effect engine. * See EffectApi.h for details on effect command() function, valid command codes * and formats. */ - status_t command(int32_t cmdCode, int32_t cmdSize, void *cmdData, int32_t *replySize, void *replyData); + virtual status_t command(int32_t cmdCode, + int32_t cmdSize, + void *cmdData, + int32_t *replySize, + void *replyData); /* @@ -409,6 +402,17 @@ public: */ static status_t guidToString(const effect_uuid_t *guid, char *str, size_t maxLen); +protected: + volatile int32_t mEnabled; // enable state + int32_t mSessionId; // audio session ID + int32_t mPriority; // priority for effect control + status_t mStatus; // effect status + effect_callback_t mCbf; // callback function for status, control and + // parameter changes notifications + void* mUserData; // client context for callback function + effect_descriptor_t mDescriptor; // effect descriptor + int32_t mId; // system wide unique effect engine instance ID + private: // Implements the IEffectClient interface @@ -419,9 +423,17 @@ private: EffectClient(AudioEffect *effect) : mEffect(effect){} // IEffectClient - virtual void controlStatusChanged(bool controlGranted) {mEffect->controlStatusChanged(controlGranted);} - virtual void enableStatusChanged(bool enabled) {mEffect->enableStatusChanged(enabled);} - virtual void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData) { + virtual void controlStatusChanged(bool controlGranted) { + mEffect->controlStatusChanged(controlGranted); + } + virtual void enableStatusChanged(bool enabled) { + mEffect->enableStatusChanged(enabled); + } + virtual void commandExecuted(int cmdCode, + int cmdSize, + void *pCmdData, + int replySize, + void *pReplyData) { mEffect->commandExecuted(cmdCode, cmdSize, pCmdData, replySize, pReplyData); } @@ -446,14 +458,6 @@ private: sp<EffectClient> mIEffectClient; // IEffectClient implementation sp<IMemory> mCblkMemory; // shared memory for deferred parameter setting effect_param_cblk_t* mCblk; // control block for deferred parameter setting - int32_t mPriority; // priority for effect control - status_t mStatus; // effect status - volatile int32_t mEnabled; // enable state - effect_callback_t mCbf; // callback function for status, control, parameter changes notifications - void* mUserData; // client context for callback function - effect_descriptor_t mDescriptor; // effect descriptor - int32_t mId; // system wide unique effect engine instance identifier - int32_t mSessionId; // audio session ID }; diff --git a/include/media/EffectVisualizerApi.h b/include/media/EffectVisualizerApi.h new file mode 100644 index 000000000000..1155db8139d1 --- /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 e892875113fd..9416ca1ab185 100644 --- a/include/media/IMediaPlayerService.h +++ b/include/media/IMediaPlayerService.h @@ -48,10 +48,6 @@ public: virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0; virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0; virtual sp<IOMX> getOMX() = 0; - - // Take a peek at currently playing audio, for visualization purposes. - // This returns a buffer of 16 bit mono PCM data, or NULL if no visualization buffer is currently available. - virtual sp<IMemory> snoop() = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/media/Visualizer.h b/include/media/Visualizer.h new file mode 100644 index 000000000000..5d51de8374f1 --- /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 62a4e50a2e68..4963f73c1df1 100644 --- a/include/media/mediaplayer.h +++ b/include/media/mediaplayer.h @@ -166,7 +166,6 @@ public: void notify(int msg, int ext1, int ext2); static sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); static sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); - static int snoop(short *data, int len, int kind); status_t invoke(const Parcel& request, Parcel *reply); status_t setMetadataFilter(const Parcel& filter); status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata); diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index 48c04a6680c1..e6f46ce9ecc0 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -17,7 +17,8 @@ #define LOG_TAG "AudioFlinger" -//#define LOG_NDEBUG 0 +// +#define LOG_NDEBUG 0 #include <math.h> #include <signal.h> @@ -52,6 +53,7 @@ #endif #include <media/EffectsFactoryApi.h> +#include <media/EffectVisualizerApi.h> // ---------------------------------------------------------------------------- // the sim build doesn't have gettid @@ -4498,6 +4500,11 @@ status_t AudioFlinger::getEffectDescriptor(effect_uuid_t *pUuid, effect_descript return EffectGetDescriptor(pUuid, descriptor); } + +// this UUID must match the one defined in media/libeffects/EffectVisualizer.cpp +static const effect_uuid_t VISUALIZATION_UUID_ = + {0xd069d9e0, 0x8329, 0x11df, 0x9168, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}; + sp<IEffect> AudioFlinger::createEffect(pid_t pid, effect_descriptor_t *pDesc, const sp<IEffectClient>& effectClient, @@ -4525,6 +4532,15 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid, { Mutex::Autolock _l(mLock); + // check recording permission for visualizer + if (memcmp(&pDesc->type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0 || + memcmp(&pDesc->uuid, &VISUALIZATION_UUID_, sizeof(effect_uuid_t)) == 0) { + if (!recordingAllowed()) { + lStatus = PERMISSION_DENIED; + goto Exit; + } + } + if (!EffectIsNullUuid(&pDesc->uuid)) { // if uuid is specified, request effect descriptor lStatus = EffectGetDescriptor(&pDesc->uuid, &desc); @@ -5089,7 +5105,7 @@ void AudioFlinger::EffectModule::process() if (mState != ACTIVE) { switch (mState) { case RESET: - reset(); + reset_l(); mState = STARTING; // clear auxiliary effect input buffer for next accumulation if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { @@ -5097,14 +5113,14 @@ void AudioFlinger::EffectModule::process() } return; case STARTING: - start(); + start_l(); mState = ACTIVE; break; case STOPPING: mState = STOPPED; break; case STOPPED: - stop(); + stop_l(); mState = IDLE; return; } @@ -5132,7 +5148,7 @@ void AudioFlinger::EffectModule::process() } } -void AudioFlinger::EffectModule::reset() +void AudioFlinger::EffectModule::reset_l() { if (mEffectInterface == NULL) { return; @@ -5205,6 +5221,7 @@ status_t AudioFlinger::EffectModule::configure() status_t AudioFlinger::EffectModule::init() { + Mutex::Autolock _l(mLock); if (mEffectInterface == NULL) { return NO_INIT; } @@ -5217,7 +5234,7 @@ status_t AudioFlinger::EffectModule::init() return status; } -status_t AudioFlinger::EffectModule::start() +status_t AudioFlinger::EffectModule::start_l() { if (mEffectInterface == NULL) { return NO_INIT; @@ -5231,7 +5248,7 @@ status_t AudioFlinger::EffectModule::start() return status; } -status_t AudioFlinger::EffectModule::stop() +status_t AudioFlinger::EffectModule::stop_l() { if (mEffectInterface == NULL) { return NO_INIT; @@ -5247,7 +5264,8 @@ status_t AudioFlinger::EffectModule::stop() status_t AudioFlinger::EffectModule::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData) { - LOGV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface); + Mutex::Autolock _l(mLock); +// LOGV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface); if (mEffectInterface == NULL) { return NO_INIT; @@ -5255,7 +5273,6 @@ status_t AudioFlinger::EffectModule::command(int cmdCode, int cmdSize, void *pCm status_t status = (*mEffectInterface)->command(mEffectInterface, cmdCode, cmdSize, pCmdData, replySize, pReplyData); if (cmdCode != EFFECT_CMD_GET_PARAM && status == NO_ERROR) { int size = (replySize == NULL) ? 0 : *replySize; - Mutex::Autolock _l(mLock); for (size_t i = 1; i < mHandles.size(); i++) { sp<EffectHandle> h = mHandles[i].promote(); if (h != 0) { @@ -5322,6 +5339,7 @@ bool AudioFlinger::EffectModule::isEnabled() status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller) { + Mutex::Autolock _l(mLock); status_t status = NO_ERROR; // Send volume indication if EFFECT_FLAG_VOLUME_IND is set and read back altered volume @@ -5347,6 +5365,7 @@ status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, status_t AudioFlinger::EffectModule::setDevice(uint32_t device) { + Mutex::Autolock _l(mLock); status_t status = NO_ERROR; if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) { // convert device bit field from AudioSystem to EffectApi format. @@ -5366,6 +5385,7 @@ status_t AudioFlinger::EffectModule::setDevice(uint32_t device) status_t AudioFlinger::EffectModule::setMode(uint32_t mode) { + Mutex::Autolock _l(mLock); status_t status = NO_ERROR; if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_MODE_MASK) == EFFECT_FLAG_AUDIO_MODE_IND) { // convert audio mode from AudioSystem to EffectApi format. @@ -5586,7 +5606,7 @@ void AudioFlinger::EffectHandle::disconnect() status_t AudioFlinger::EffectHandle::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData) { - LOGV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p", cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get()); +// LOGV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p", cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get()); // only get parameter command is permitted for applications not controlling the effect if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) { diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h index 42dca4c97ece..ec3d7f1ba12c 100644 --- a/libs/audioflinger/AudioFlinger.h +++ b/libs/audioflinger/AudioFlinger.h @@ -916,7 +916,7 @@ private: void process(); status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData); - void reset(); + void reset_l(); status_t configure(); status_t init(); uint32_t state() { @@ -951,8 +951,8 @@ private: EffectModule(const EffectModule&); EffectModule& operator = (const EffectModule&); - status_t start(); - status_t stop(); + status_t start_l(); + status_t stop_l(); // update this table when AudioSystem::audio_devices or audio_device_e (in EffectApi.h) are modified static const uint32_t sDeviceConvTable[]; diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index 882ad8388b9a..bcda45e74e14 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 @@ void LayerCache::clear() { 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 519552de274c..adc671325f94 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 @@ public: 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 ae7719c45cd5..027ed7911810 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -34,9 +34,6 @@ namespace uirenderer { // 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 @@ namespace uirenderer { #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 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { 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 @@ int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bot 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()); - 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; + GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0; + LayerSize size(right - left, bottom - top); - return false; - } - } else { - LAYER_LOGD("Reusing layer"); - glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); + // TODO VERY IMPORTANT: Fix TextView to not call saveLayer() all the time + Layer* layer = mLayerCache.get(size, previousFbo); + if (!layer) { + 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 Binary files differnew file mode 100644 index 000000000000..81a87b1726c2 --- /dev/null +++ b/libs/rs/java/ImageProcessing/res/drawable/data.jpg diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh index 8bf53bf2666c..370929676a2b 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 inpu 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 b1b7fed0f67d..053cc22c22ce 100644 --- a/media/java/android/media/AudioEffect.java +++ b/media/java/android/media/AudioEffect.java @@ -27,22 +27,25 @@ import java.nio.ByteBuffer; import java.util.UUID; /** - * AudioEffect is the base class for implementing audio effect control in Java applications. - * Creating an AudioEffect object will create the effect engine in audio framework if no - * instance of the same effect type exists in the specified audio session. - * If one exists, this instance will be used. The application creating the AudioEffect object - * (or a derived class) will either receive control of the effect engine or not depending - * on the priority parameter. If priority is higher than the priority used by the current - * effect engine owner, the control will be transfered to the new object. Otherwise - * control will remain with the previous object. In this case, the new application will be - * notified of changes in effect engine state or control ownership by the appropiate listener. - * If the effect is to be applied to a specific AudioTrack or MediaPlayer instance, - * the application must specify the audio session ID of that instance. + * AudioEffect is the base class for implementing audio effect control in Java + * applications. + * <p>Creating an AudioEffect object will create the effect engine in + * audio framework if no instance of the same effect type exists in the + * specified audio session. If one exists, this instance will be used. + * <p>The application creating the AudioEffect object (or a derived class) will either + * receive control of the effect engine or not depending on the priority + * parameter. If priority is higher than the priority used by the current effect + * engine owner, the control will be transfered to the new object. Otherwise + * control will remain with the previous object. In this case, the new + * application will be notified of changes in effect engine state or control + * ownership by the appropiate listener. + * <p>If the effect is to be applied to a specific AudioTrack or MediaPlayer instance, + * the application must specify the audio session ID of that instance when calling the AudioEffect + * constructor. * - * {@hide Pending API council review} + * { @hide Pending API council review } */ -public class AudioEffect -{ +public class AudioEffect { static { System.loadLibrary("audioeffect_jni"); native_init(); @@ -51,31 +54,60 @@ public class AudioEffect private final static String TAG = "AudioEffect-JAVA"; /** - * The following UUIDs define effect types corresponding to standard audio effects - * whose implementation and interface conform to the OpenSL ES specification. - * The definitions match the corresponding interface IDs in OpenSLES_IID.h + * The following UUIDs define effect types corresponding to standard audio + * effects whose implementation and interface conform to the OpenSL ES + * specification. The definitions match the corresponding interface IDs in + * OpenSLES_IID.h */ - public static final UUID EFFECT_TYPE_ENV_REVERB = UUID.fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e"); - public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID.fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b"); - public static final UUID EFFECT_TYPE_EQUALIZER = UUID.fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b"); - public static final UUID EFFECT_TYPE_BASS_BOOST = UUID.fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b"); - public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID.fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b"); - public static final UUID EFFECT_TYPE_INVALID = UUID.fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210"); + /** + * UUID for environmental reverb effect + */ + public static final UUID EFFECT_TYPE_ENV_REVERB = UUID + .fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e"); + /** + * UUID for preset reverb effect + */ + public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID + .fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b"); + /** + * UUID for equalizer effect + */ + public static final UUID EFFECT_TYPE_EQUALIZER = UUID + .fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b"); + /** + * UUID for bass boost effect + */ + public static final UUID EFFECT_TYPE_BASS_BOOST = UUID + .fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b"); + /** + * UUID for virtualizer effect + */ + public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID + .fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b"); /** - * State of an AudioEffect object that was not successfully initialized upon creation + * Null effect UUID. Used when the UUID for effect type of + */ + public static final UUID EFFECT_TYPE_NULL = UUID + .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210"); + + /** + * State of an AudioEffect object that was not successfully initialized upon + * creation */ public static final int STATE_UNINITIALIZED = 0; /** * State of an AudioEffect object that is ready to be used. */ - public static final int STATE_INITIALIZED = 1; + public static final int STATE_INITIALIZED = 1; + // to keep in sync with + // frameworks/base/media/jni/audioeffect/android_media_AudioEffect.cpp /** * Event id for engine state change notification. */ - protected static final int NATIVE_EVENT_ENABLED_STATUS = 0; + protected static final int NATIVE_EVENT_ENABLED_STATUS = 0; /** * Event id for engine control ownership change notification. */ @@ -85,56 +117,89 @@ public class AudioEffect */ protected static final int NATIVE_EVENT_PARAMETER_CHANGED = 2; + /** + * Successful operation. + */ + public static final int SUCCESS = 0; + /** + * Unspecified error. + */ + public static final int ERROR = -1; + /** + * Internal opreation status. Not returned by any method. + */ + public static final int ALREADY_EXISTS = -2; + /** + * Operation failed due to bad object initialization. + */ + public static final int ERROR_NO_INIT = -3; + /** + * Operation failed due to bad parameter value. + */ + public static final int ERROR_BAD_VALUE = -4; + /** + * Operation failed because it was requested in wrong state. + */ + public static final int ERROR_INVALID_OPERATION = -5; + /** + * Operation failed due to lack of memory. + */ + public static final int ERROR_NO_MEMORY = -6; + /** + * Operation failed due to dead remote object. + */ + public static final int ERROR_DEAD_OBJECT = -7; + + /** + * The effect descriptor contains necessary information to facilitate + * effects enumeration:<br> + * <ul> + * <li>mType: UUID corresponding to the OpenSL ES interface implemented by this effect</li> + * <li>mUuid: UUID for this particular implementation</li> + * <li>mConnectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li> + * <li>mName: human readable effect name</li> + * <li>mImplementor: human readable effect implementor name</li> + * </ul> + */ + public static class Descriptor { + + public Descriptor() { + } + + public Descriptor(String type, String uuid, String connectMode, + String name, String implementor) { + mType = UUID.fromString(type); + mUuid = UUID.fromString(uuid); + mConnectMode = connectMode; + mName = name; + mImplementor = implementor; + } + + public UUID mType; + public UUID mUuid; + public String mConnectMode; + public String mName; + public String mImplementor; + }; + + /** + * Effect connection mode is insert. Specifying an audio session ID when creating the effect + * will insert this effect after all players in the same audio session. + */ + public static final String EFFECT_INSERT = "Insert"; + /** + * Effect connection mode is auxiliary. + * <p>Auxiliary effects must be created on session 0 (global output mix). In order for a + * MediaPlayer or AudioTrack to be fed into this effect, they must be explicitely attached to + * this effect and a send level must be specified. + * <p>Use the effect ID returned by {@link #getId()} to designate this particular effect when + * attaching it to the MediaPlayer or AudioTrack. + */ + public static final String EFFECT_AUXILIARY = "Auxiliary"; - // to keep in sync with frameworks/base/media/jni/audioeffect/android_media_AudioEffect.cpp - public static final int SUCCESS = 0; - public static final int ERROR = -1; - public static final int ALREADY_EXISTS = -2; - public static final int NO_INIT = -3; - public static final int BAD_VALUE = -4; - public static final int INVALID_OPERATION = -5; - public static final int NO_MEMORY = -6; - public static final int DEAD_OBJECT = -7; - - - /** - * The effect descriptor contains necessary information to facilitate - * effects enumeration: - * mType: UUID corresponding to the OpenSL ES interface implemented by this effect - * mUuid: UUID for this particular implementation - * mConnectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY} - * mName: human readable effect name - * mImplementor: human readable effect implementor name - */ - public static class Descriptor { - - public Descriptor() { - } - public Descriptor(String type, - String uuid, - String connectMode, - String name, - String implementor) { - mType = UUID.fromString(type); - mUuid = UUID.fromString(uuid); - mConnectMode = connectMode; - mName = name; - mImplementor = implementor; - } - - public UUID mType; - public UUID mUuid; - public String mConnectMode; - public String mName; - public String mImplementor; - }; - - public static final String EFFECT_INSERT = "Insert"; - public static final String EFFECT_AUXILIARY = "Auxiliary"; - - //-------------------------------------------------------------------------- + // -------------------------------------------------------------------------- // Member variables - //-------------------- + // -------------------- /** * Indicates the state of the AudioEffect instance */ @@ -159,17 +224,20 @@ public class AudioEffect /** * Listener for effect engine state change notifications. - * @see #setEnableStatusListener(OnEnableStatusChangeListener) + * + * @see #setEnableStatusListener(OnEnableStatusChangeListener) */ protected OnEnableStatusChangeListener mEnableStatusChangeListener = null; /** * Listener for effect engine control ownership change notifications. - * @see #setControlStatusListener(OnControlStatusChangeListener) + * + * @see #setControlStatusListener(OnControlStatusChangeListener) */ protected OnControlStatusChangeListener mControlChangeStatusListener = null; /** * Listener for effect engine control ownership change notifications. - * @see #setParameterListener(OnParameterChangeListener) + * + * @see #setParameterListener(OnParameterChangeListener) */ protected OnParameterChangeListener mParameterChangeListener = null; /** @@ -181,32 +249,36 @@ public class AudioEffect */ protected NativeEventHandler mNativeEventHandler = null; - - - //-------------------------------------------------------------------------- + // -------------------------------------------------------------------------- // Constructor, Finalize - //-------------------- + // -------------------- /** * Class constructor. - * @param type: type of effect engine created. See - * {@link #EFFECT_TYPE_ENV_REVERB}, {@link #EFFECT_TYPE_EQUALIZER} ... - * Types corresponding to built-in effects are defined by AudioEffect class. - * Other types can be specified provided they correspond an existing OpenSL ES - * interface ID and the corresponsing effect is available on the platform. - * If an unspecified effect type is requested, the constructor with throw the - * IllegalArgumentException. - * @param uuid: unique identifier of a particular effect implementation. Must be - * specified if the caller wants to use a particular implementation of an effect type. - * This parameter can be set to null in which case only the type will be used to select - * the effect. - * @param priority: the priority level requested by the application for controlling - * the effect engine. As the same effect engine can be shared by several applications, - * this parameter indicates how much the requesting application needs control of - * effect parameters. The normal priority is 0, above normal is a positive number, - * below normal a negative number. - * @param audioSession: System wide unique audio session identifier. If audioSession - * is not 0, the effect will be attached to the MediaPlayer or AudioTrack in the - * same audio session. Otherwise, the effect will apply to the output mix. + * + * @param type type of effect engine created. See {@link #EFFECT_TYPE_ENV_REVERB}, + * {@link #EFFECT_TYPE_EQUALIZER} ... Types corresponding to + * built-in effects are defined by AudioEffect class. Other types + * can be specified provided they correspond an existing OpenSL + * ES interface ID and the corresponsing effect is available on + * the platform. If an unspecified effect type is requested, the + * constructor with throw the IllegalArgumentException. This + * parameter can be set to {@link #EFFECT_TYPE_NULL} in which + * case only the uuid will be used to select the effect. + * @param uuid unique identifier of a particular effect implementation. + * Must be specified if the caller wants to use a particular + * implementation of an effect type. This parameter can be set to + * {@link #EFFECT_TYPE_NULL} in which case only the type will + * be used to select the effect. + * @param priority the priority level requested by the application for + * controlling the effect engine. As the same effect engine can + * be shared by several applications, this parameter indicates + * how much the requesting application needs control of effect + * parameters. The normal priority is 0, above normal is a + * positive number, below normal a negative number. + * @param audioSession System wide unique audio session identifier. If audioSession + * is not 0, the effect will be attached to the MediaPlayer or + * AudioTrack in the same audio session. Otherwise, the effect + * will apply to the output mix. * * @throws java.lang.IllegalArgumentException * @throws java.lang.UnsupportedOperationException @@ -214,22 +286,28 @@ public class AudioEffect */ public AudioEffect(UUID type, UUID uuid, int priority, int audioSession) - throws IllegalArgumentException, UnsupportedOperationException, RuntimeException { + throws IllegalArgumentException, UnsupportedOperationException, + RuntimeException { int[] id = new int[1]; Descriptor[] desc = new Descriptor[1]; // native initialization int initResult = native_setup(new WeakReference<AudioEffect>(this), - type.toString(), uuid.toString(), priority, audioSession, id, desc); + type.toString(), uuid.toString(), priority, audioSession, id, + desc); if (initResult != SUCCESS && initResult != ALREADY_EXISTS) { - Log.e(TAG, "Error code "+initResult+" when initializing AudioEffect."); + Log.e(TAG, "Error code " + initResult + + " when initializing AudioEffect."); switch (initResult) { - case BAD_VALUE: - throw (new IllegalArgumentException("Effect type: "+type+ " not supported.")); - case INVALID_OPERATION: - throw (new UnsupportedOperationException("Effect library not loaded")); + case ERROR_BAD_VALUE: + throw (new IllegalArgumentException("Effect type: " + type + + " not supported.")); + case ERROR_INVALID_OPERATION: + throw (new UnsupportedOperationException( + "Effect library not loaded")); default: - throw (new RuntimeException("Cannot initialize effect engine for type: "+type+ - "Error: "+ initResult)); + throw (new RuntimeException( + "Cannot initialize effect engine for type: " + type + + "Error: " + initResult)); } } mId = id[0]; @@ -240,9 +318,9 @@ public class AudioEffect } /** - * Releases the native AudioEffect resources. It is a good practice to release the - * effect engine when not in use as control can be returned to other applications - * or the native resources released. + * Releases the native AudioEffect resources. It is a good practice to + * release the effect engine when not in use as control can be returned to + * other applications or the native resources released. */ public void release() { synchronized (mStateLock) { @@ -258,119 +336,115 @@ public class AudioEffect /** * Get the effect descriptor. - * {@see #Descriptor}. + * + //TODO when AudioEffect class is unhidden @ see android.media.AudioEffect.Descriptor * @throws IllegalStateException */ - public Descriptor getDescriptor() - throws IllegalStateException { + public Descriptor getDescriptor() throws IllegalStateException { checkState("getDescriptor()"); return mDescriptor; } - //-------------------------------------------------------------------------- + // -------------------------------------------------------------------------- // Effects Enumeration - //-------------------- + // -------------------- /** * Query all effects available on the platform. Returns an array of - * {@link #Descriptor} objects + //TODO when AudioEffect class is unhidden: {@ link android.media.AudioEffect.Descriptor} objects * * @throws IllegalStateException */ static public Descriptor[] queryEffects() { - return (Descriptor[])native_query_effects(); + return (Descriptor[]) native_query_effects(); } - //-------------------------------------------------------------------------- + // -------------------------------------------------------------------------- // Control methods - //-------------------- - - /** - * Enable effect engine. - * @return {@link #NO_ERROR} in case of success, - * {@link #INVALID_OPERATION} or {@link #DEAD_OBJECT} in case of failure. - * @throws IllegalStateException - */ - public int enable() - throws IllegalStateException { - checkState("enable()"); - return native_enable(); - } + // -------------------- /** - * Disable effect engine. - * @return NO_ERROR in case of success, - * INVALID_OPERATION or DEAD_OBJECT in case of failure. + * Enable or disable effect engine. + * + * @param enabled the requested enable state + * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION} + * or {@link #ERROR_DEAD_OBJECT} in case of failure. * @throws IllegalStateException */ - public int disable() - throws IllegalStateException { - checkState("disable()"); - return native_disable(); + public int setEnabled(boolean enabled) throws IllegalStateException { + checkState("setEnabled()"); + return native_setEnabled(enabled); } /** * Set effect parameter. The setParameter method is provided in several - * forms addressing most common parameter formats. This form is the - * most generic one where the parameter and its value are both specified - * as an array of bytes. The parameter and value type and length are therefore - * totally free. For standard effect defined by OpenSL ES, the parameter format - * and values must match the definitions in the corresponding OpenSL ES interface. + * forms addressing most common parameter formats. This form is the most + * generic one where the parameter and its value are both specified as an + * array of bytes. The parameter and value type and length are therefore + * totally free. For standard effect defined by OpenSL ES, the parameter + * format and values must match the definitions in the corresponding OpenSL + * ES interface. * - * @param param: the identifier of the parameter to set - * @param value: the new value for the specified parameter - * @return NO_ERROR in case of success, - * {@link #BAD_VALUE}, {@link #NO_MEMORY}, {@link #INVALID_OPERATION} or {@link DEAD_OBJECT} in case of failure + * @param param the identifier of the parameter to set + * @param value the new value for the specified parameter + * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE}, + * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or + * {@link #ERROR_DEAD_OBJECT} in case of failure * @throws IllegalStateException */ public int setParameter(byte[] param, byte[] value) - throws IllegalStateException { + throws IllegalStateException { checkState("setParameter()"); return native_setParameter(param.length, param, value.length, value); } /** * Set effect parameter. The parameter and its value are integers. - * @see #setParameter(byte[], byte[]) + * + * @see #setParameter(byte[], byte[]) */ - public int setParameter(int param, int value) - throws IllegalStateException { + public int setParameter(int param, int value) throws IllegalStateException { byte[] p = intToByteArray(param); byte[] v = intToByteArray(value); return setParameter(p, v); } /** - * Set effect parameter. The parameter is an integer and the value is a short integer. - * @see #setParameter(byte[], byte[]) + * Set effect parameter. The parameter is an integer and the value is a + * short integer. + * + * @see #setParameter(byte[], byte[]) */ public int setParameter(int param, short value) - throws IllegalStateException { + throws IllegalStateException { byte[] p = intToByteArray(param); byte[] v = shortToByteArray(value); return setParameter(p, v); } /** - * Set effect parameter. The parameter is an integer and the value is an array of bytes. - * @see #setParameter(byte[], byte[]) + * Set effect parameter. The parameter is an integer and the value is an + * array of bytes. + * + * @see #setParameter(byte[], byte[]) */ public int setParameter(int param, byte[] value) - throws IllegalStateException { + throws IllegalStateException { byte[] p = intToByteArray(param); return setParameter(p, value); } /** - * Set effect parameter. The parameter is an array of 1 or 2 integers and the value - * is also an array of 1 or 2 integers - * @see #setParameter(byte[], byte[]) + * Set effect parameter. The parameter is an array of 1 or 2 integers and + * the value is also an array of 1 or 2 integers + * + * @see #setParameter(byte[], byte[]) */ public int setParameter(int[] param, int[] value) - throws IllegalStateException { + throws IllegalStateException { if (param.length > 2 || value.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param[0]); if (param.length > 1) { @@ -386,14 +460,15 @@ public class AudioEffect } /** - * Set effect parameter. The parameter is an array of 1 or 2 integers and the value - * is an array of 1 or 2 short integers - * @see #setParameter(byte[], byte[]) + * Set effect parameter. The parameter is an array of 1 or 2 integers and + * the value is an array of 1 or 2 short integers + * + * @see #setParameter(byte[], byte[]) */ public int setParameter(int[] param, short[] value) - throws IllegalStateException { + throws IllegalStateException { if (param.length > 2 || value.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param[0]); if (param.length > 1) { @@ -410,14 +485,15 @@ public class AudioEffect } /** - * Set effect parameter. The parameter is an array of 1 or 2 integers and the value - * is an array of bytes - * @see #setParameter(byte[], byte[]) + * Set effect parameter. The parameter is an array of 1 or 2 integers and + * the value is an array of bytes + * + * @see #setParameter(byte[], byte[]) */ public int setParameter(int[] param, byte[] value) - throws IllegalStateException { + throws IllegalStateException { if (param.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param[0]); if (param.length > 1) { @@ -429,20 +505,23 @@ public class AudioEffect /** * Get effect parameter. The getParameter method is provided in several - * forms addressing most common parameter formats. This form is the - * most generic one where the parameter and its value are both specified - * as an array of bytes. The parameter and value type and length are therefore + * forms addressing most common parameter formats. This form is the most + * generic one where the parameter and its value are both specified as an + * array of bytes. The parameter and value type and length are therefore * totally free. - * @param param: the identifier of the parameter to set - * @param value: the new value for the specified parameter - * @return NO_ERROR in case of success, - * {@link #BAD_VALUE}, {@link #NO_MEMORY}, {@link #INVALID_OPERATION} or {@link DEAD_OBJECT} in case of failure - * When called, value.length indicates the maximum size of the returned parameters value. - * When returning, value.length is updated with the actual size of the returned value. + * + * @param param the identifier of the parameter to set + * @param value the new value for the specified parameter + * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE}, + * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or + * {@link #ERROR_DEAD_OBJECT} in case of failure When called, value.length + * indicates the maximum size of the returned parameters value. When + * returning, value.length is updated with the actual size of the + * returned value. * @throws IllegalStateException */ public int getParameter(byte[] param, byte[] value) - throws IllegalStateException { + throws IllegalStateException { checkState("getParameter()"); int[] vSize = new int[1]; vSize[0] = value.length; @@ -456,25 +535,28 @@ public class AudioEffect } /** - * Get effect parameter. The parameter is an integer and the value is an array of bytes. - * @see #getParameter(byte[], byte[]) + * Get effect parameter. The parameter is an integer and the value is an + * array of bytes. + * + * @see #getParameter(byte[], byte[]) */ public int getParameter(int param, byte[] value) - throws IllegalStateException { + throws IllegalStateException { byte[] p = intToByteArray(param); return getParameter(p, value); } /** - * Get effect parameter. The parameter is an integer and the value - * is an array of 1 or 2 integers - * @see #getParameter(byte[], byte[]) + * Get effect parameter. The parameter is an integer and the value is an + * array of 1 or 2 integers + * + * @see #getParameter(byte[], byte[]) */ public int getParameter(int param, int[] value) - throws IllegalStateException { + throws IllegalStateException { if (value.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param); @@ -490,14 +572,15 @@ public class AudioEffect } /** - * Get effect parameter. The parameter is an integer and the value - * is an array of 1 or 2 short integers - * @see #getParameter(byte[], byte[]) + * Get effect parameter. The parameter is an integer and the value is an + * array of 1 or 2 short integers + * + * @see #getParameter(byte[], byte[]) */ public int getParameter(int param, short[] value) - throws IllegalStateException { + throws IllegalStateException { if (value.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param); @@ -513,14 +596,15 @@ public class AudioEffect } /** - * Get effect parameter. The parameter is an array of 1 or 2 integers and the value - * is also an array of 1 or 2 integers - * @see #getParameter(byte[], byte[]) + * Get effect parameter. The parameter is an array of 1 or 2 integers and + * the value is also an array of 1 or 2 integers + * + * @see #getParameter(byte[], byte[]) */ public int getParameter(int[] param, int[] value) - throws IllegalStateException { + throws IllegalStateException { if (param.length > 2 || value.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param[0]); if (param.length > 1) { @@ -539,14 +623,15 @@ public class AudioEffect } /** - * Get effect parameter. The parameter is an array of 1 or 2 integers and the value - * is an array of 1 or 2 short integers - * @see #getParameter(byte[], byte[]) + * Get effect parameter. The parameter is an array of 1 or 2 integers and + * the value is an array of 1 or 2 short integers + * + * @see #getParameter(byte[], byte[]) */ public int getParameter(int[] param, short[] value) - throws IllegalStateException { + throws IllegalStateException { if (param.length > 2 || value.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param[0]); if (param.length > 1) { @@ -565,14 +650,15 @@ public class AudioEffect } /** - * Get effect parameter. The parameter is an array of 1 or 2 integers and the value - * is an array of bytes - * @see #getParameter(byte[], byte[]) + * Get effect parameter. The parameter is an array of 1 or 2 integers and + * the value is an array of bytes + * + * @see #getParameter(byte[], byte[]) */ public int getParameter(int[] param, byte[] value) - throws IllegalStateException { + throws IllegalStateException { if (param.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param[0]); if (param.length > 1) { @@ -583,19 +669,19 @@ public class AudioEffect return getParameter(p, value); } - /** - * Send a command to the effect engine. This method is intended to send proprietary - * commands to a particular effect implementation. + * Send a command to the effect engine. This method is intended to send + * proprietary commands to a particular effect implementation. * */ public int command(int cmdCode, byte[] command, byte[] reply) - throws IllegalStateException { + throws IllegalStateException { checkState("command()"); int[] replySize = new int[1]; replySize[0] = reply.length; - int status = native_command(cmdCode, command.length, command, replySize, reply); + int status = native_command(cmdCode, command.length, command, + replySize, reply); if (reply.length > replySize[0]) { byte[] resizedReply = new byte[replySize[0]]; @@ -605,51 +691,53 @@ public class AudioEffect return status; } - //-------------------------------------------------------------------------- + // -------------------------------------------------------------------------- // Getters - //-------------------- + // -------------------- /** - * Returns effect unique identifier. This system wide unique identifier - * can be used to attach this effect to a MediaPlayer or an AudioTrack - * when the effect is an auxiliary effect (Reverb) + * Returns effect unique identifier. This system wide unique identifier can + * be used to attach this effect to a MediaPlayer or an AudioTrack when the + * effect is an auxiliary effect (Reverb) + * * @return the effect identifier. * @throws IllegalStateException */ - public int getId() - throws IllegalStateException { + public int getId() throws IllegalStateException { checkState("getId()"); return mId; } /** * Returns effect engine enable state + * * @return true if the effect is enabled, false otherwise. * @throws IllegalStateException */ - public boolean getEnable() - throws IllegalStateException { - checkState("getEnable()"); - return native_getEnable(); + public boolean getEnabled() throws IllegalStateException { + checkState("getEnabled()"); + return native_getEnabled(); } /** * Checks if this AudioEffect object is controlling the effect engine. - * @return true if this instance has control of effect engine, false otherwise. + * + * @return true if this instance has control of effect engine, false + * otherwise. * @throws IllegalStateException */ - public boolean hasControl() - throws IllegalStateException { + public boolean hasControl() throws IllegalStateException { checkState("hasControl()"); return native_hasControl(); } - //-------------------------------------------------------------------------- + // -------------------------------------------------------------------------- // Initialization / configuration - //-------------------- + // -------------------- /** * Sets the listener AudioEffect notifies when the effect engine is enabled * or disabled. + * * @param listener */ public void setEnableStatusListener(OnEnableStatusChangeListener listener) { @@ -662,8 +750,9 @@ public class AudioEffect } /** - * Sets the listener AudioEffect notifies when the effect engine control - * is taken or returned. + * Sets the listener AudioEffect notifies when the effect engine control is + * taken or returned. + * * @param listener */ public void setControlStatusListener(OnControlStatusChangeListener listener) { @@ -677,6 +766,7 @@ public class AudioEffect /** * Sets the listener AudioEffect notifies when a parameter is changed. + * * @param listener */ public void setParameterListener(OnParameterChangeListener listener) { @@ -691,7 +781,7 @@ public class AudioEffect // Convenience method for the creation of the native event handler // It is called only when a non-null event listener is set. // precondition: - // mNativeEventHandler is null + // mNativeEventHandler is null private void createNativeEventHandler() { Looper looper; if ((looper = Looper.myLooper()) != null) { @@ -703,52 +793,62 @@ public class AudioEffect } } - //--------------------------------------------------------- + // --------------------------------------------------------- // Interface definitions - //-------------------- + // -------------------- /** - * Interface definition for a callback to be invoked when the - * effect engine is enabled or disabled. + * The OnParameterChangeListener interface defines a method called by the AudioEffect + * when a the enabled state of the effect engine was changed by the controlling application. */ - public interface OnEnableStatusChangeListener { + public interface OnEnableStatusChangeListener { /** - * Called on the listener to notify it that the effect engine - * has been enabled or disabled. + * Called on the listener to notify it that the effect engine has been + * enabled or disabled. + * @param effect the effect on which the interface is registered. + * @param enabled new effect state. */ void onEnableStatusChange(AudioEffect effect, boolean enabled); } /** - * Interface definition for a callback to be invoked when the - * effect engine control is taken or returned. + * The OnControlStatusChangeListener interface defines a method called by the AudioEffect + * when a the control of the effect engine is gained or lost by the application */ - public interface OnControlStatusChangeListener { + public interface OnControlStatusChangeListener { /** - * Called on the listener to notify it that the effect engine - * control has been taken or returned. + * Called on the listener to notify it that the effect engine control + * has been taken or returned. + * @param effect the effect on which the interface is registered. + * @param controlGranted true if the application has been granted control of the effect + * engine, false otherwise. */ void onControlStatusChange(AudioEffect effect, boolean controlGranted); } /** - * Interface definition for a callback to be invoked when a - * parameter value has changed. + * The OnParameterChangeListener interface defines a method called by the AudioEffect + * when a parameter is changed in the effect engine by the controlling application. */ - public interface OnParameterChangeListener { + public interface OnParameterChangeListener { /** * Called on the listener to notify it that a parameter value has changed. + * @param effect the effect on which the interface is registered. + * @param status status of the set parameter operation. + * @param param ID of the modified parameter. + * @param value the new parameter value. */ - void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value); + void onParameterChange(AudioEffect effect, int status, byte[] param, + byte[] value); } - //--------------------------------------------------------- + // --------------------------------------------------------- // Inner classes - //-------------------- + // -------------------- /** - * Helper class to handle the forwarding of native events to the appropriate listeners + * Helper class to handle the forwarding of native events to the appropriate + * listeners */ - private class NativeEventHandler extends Handler - { + private class NativeEventHandler extends Handler { private AudioEffect mAudioEffect; public NativeEventHandler(AudioEffect ae, Looper looper) { @@ -761,14 +861,15 @@ public class AudioEffect if (mAudioEffect == null) { return; } - switch(msg.what) { + switch (msg.what) { case NATIVE_EVENT_ENABLED_STATUS: OnEnableStatusChangeListener enableStatusChangeListener = null; synchronized (mListenerLock) { enableStatusChangeListener = mAudioEffect.mEnableStatusChangeListener; } if (enableStatusChangeListener != null) { - enableStatusChangeListener.onEnableStatusChange(mAudioEffect, (boolean)(msg.arg1 != 0)); + enableStatusChangeListener.onEnableStatusChange( + mAudioEffect, (boolean) (msg.arg1 != 0)); } break; case NATIVE_EVENT_CONTROL_STATUS: @@ -777,7 +878,8 @@ public class AudioEffect controlStatusChangeListener = mAudioEffect.mControlChangeStatusListener; } if (controlStatusChangeListener != null) { - controlStatusChangeListener.onControlStatusChange(mAudioEffect, (boolean)(msg.arg1 != 0)); + controlStatusChangeListener.onControlStatusChange( + mAudioEffect, (boolean) (msg.arg1 != 0)); } break; case NATIVE_EVENT_PARAMETER_CHANGED: @@ -786,10 +888,12 @@ public class AudioEffect parameterChangeListener = mAudioEffect.mParameterChangeListener; } if (parameterChangeListener != null) { - // arg1 contains offset of parameter value from start of byte array + // arg1 contains offset of parameter value from start of + // byte array int vOffset = msg.arg1; - byte[] p = (byte[])msg.obj; - // See effect_param_t in EffectApi.h for psize and vsize fields offsets + byte[] p = (byte[]) msg.obj; + // See effect_param_t in EffectApi.h for psize and vsize + // fields offsets int status = byteArrayToInt(p, 0); int psize = byteArrayToInt(p, 4); int vsize = byteArrayToInt(p, 8); @@ -798,90 +902,76 @@ public class AudioEffect System.arraycopy(p, 12, param, 0, psize); System.arraycopy(p, vOffset, value, 0, vsize); - parameterChangeListener.onParameterChange(mAudioEffect, status, param, value); + parameterChangeListener.onParameterChange(mAudioEffect, + status, param, value); } break; - default: + default: Log.e(TAG, "handleMessage() Unknown event type: " + msg.what); break; } } } - - //--------------------------------------------------------- + // --------------------------------------------------------- // Java methods called from the native side - //-------------------- + // -------------------- @SuppressWarnings("unused") - private static void postEventFromNative(Object effect_ref, - int what, int arg1, int arg2, Object obj) { - AudioEffect effect = (AudioEffect)((WeakReference)effect_ref).get(); + private static void postEventFromNative(Object effect_ref, int what, + int arg1, int arg2, Object obj) { + AudioEffect effect = (AudioEffect) ((WeakReference) effect_ref).get(); if (effect == null) { return; } if (effect.mNativeEventHandler != null) { - Message m = effect.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); + Message m = effect.mNativeEventHandler.obtainMessage(what, arg1, + arg2, obj); effect.mNativeEventHandler.sendMessage(m); } } - - //--------------------------------------------------------- + // --------------------------------------------------------- // Native methods called from the Java side - //-------------------- + // -------------------- private static native final void native_init(); - private native final int native_setup(Object audioeffect_this, - String type, - String uuid, - int priority, - int audioSession, - int[] id, - Object[] desc); + private native final int native_setup(Object audioeffect_this, String type, + String uuid, int priority, int audioSession, int[] id, Object[] desc); private native final void native_finalize(); private native final void native_release(); - private native final int native_enable(); + private native final int native_setEnabled(boolean enabled); - private native final int native_disable(); - - private native final boolean native_getEnable(); + private native final boolean native_getEnabled(); private native final boolean native_hasControl(); - private native final int native_setParameter(int psize, - byte[] param, - int vsize, - byte[] value); + private native final int native_setParameter(int psize, byte[] param, + int vsize, byte[] value); - private native final int native_getParameter(int psize, - byte[] param, - int[] vsize, - byte[] value); + private native final int native_getParameter(int psize, byte[] param, + int[] vsize, byte[] value); - private native final int native_command(int cmdCode, - int cmdSize, - byte[] cmdData, - int[] repSize, - byte[] repData); + private native final int native_command(int cmdCode, int cmdSize, + byte[] cmdData, int[] repSize, byte[] repData); private static native Object[] native_query_effects(); - //--------------------------------------------------------- + // --------------------------------------------------------- // Utility methods - //------------------ + // ------------------ - protected void checkState(String methodName) - throws IllegalStateException { + protected void checkState(String methodName) throws IllegalStateException { synchronized (mStateLock) { if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException(methodName+" called on uninitialized AudioEffect.")); + throw (new IllegalStateException(methodName + + " called on uninitialized AudioEffect.")); } } } @@ -890,10 +980,12 @@ public class AudioEffect switch (status) { case AudioEffect.SUCCESS: break; - case AudioEffect.BAD_VALUE: - throw (new IllegalArgumentException("AudioEffect: bad parameter value")); - case AudioEffect.INVALID_OPERATION: - throw (new UnsupportedOperationException("AudioEffect: invalid parameter operation")); + case AudioEffect.ERROR_BAD_VALUE: + throw (new IllegalArgumentException( + "AudioEffect: bad parameter value")); + case AudioEffect.ERROR_INVALID_OPERATION: + throw (new UnsupportedOperationException( + "AudioEffect: invalid parameter operation")); default: throw (new RuntimeException("AudioEffect: set/get parameter error")); } @@ -903,6 +995,7 @@ public class AudioEffect return byteArrayToInt(valueBuf, 0); } + protected int byteArrayToInt(byte[] valueBuf, int offset) { ByteBuffer converter = ByteBuffer.wrap(valueBuf); converter.order(ByteOrder.nativeOrder()); @@ -931,12 +1024,12 @@ public class AudioEffect protected byte[] shortToByteArray(short value) { ByteBuffer converter = ByteBuffer.allocate(2); converter.order(ByteOrder.nativeOrder()); - short sValue = (short)value; + short sValue = (short) value; converter.putShort(sValue); return converter.array(); } - protected byte[] concatArrays(byte[] ...arrays) { + protected byte[] concatArrays(byte[]... arrays) { int len = 0; for (byte[] a : arrays) { len += a.length; diff --git a/media/java/android/media/Visualizer.java b/media/java/android/media/Visualizer.java new file mode 100755 index 000000000000..cdd3cdfb9334 --- /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 ca88bccfd08c..47f1974017af 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -681,18 +681,6 @@ android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz) } static jint -android_media_MediaPlayer_snoop(JNIEnv* env, jobject thiz, jobject data, jint kind) { - jshort* ar = (jshort*)env->GetPrimitiveArrayCritical((jarray)data, 0); - jsize len = env->GetArrayLength((jarray)data); - int ret = 0; - if (ar) { - ret = MediaPlayer::snoop(ar, len, kind); - env->ReleasePrimitiveArrayCritical((jarray)data, ar, 0); - } - return ret; -} - -static jint android_media_MediaPlayer_native_suspend_resume( JNIEnv *env, jobject thiz, jboolean isSuspend) { LOGV("suspend_resume(%d)", isSuspend); @@ -757,7 +745,6 @@ static JNINativeMethod gMethods[] = { {"native_init", "()V", (void *)android_media_MediaPlayer_native_init}, {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup}, {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, - {"snoop", "([SI)I", (void *)android_media_MediaPlayer_snoop}, {"native_suspend_resume", "(Z)I", (void *)android_media_MediaPlayer_native_suspend_resume}, {"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id}, {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id}, diff --git a/media/jni/audioeffect/Android.mk b/media/jni/audioeffect/Android.mk index d03b63be8237..4c5cf71dc8b4 100644 --- a/media/jni/audioeffect/Android.mk +++ b/media/jni/audioeffect/Android.mk @@ -2,7 +2,8 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - android_media_AudioEffect.cpp + android_media_AudioEffect.cpp \ + android_media_Visualizer.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp index 17f2d8fdd485..02474a490829 100644 --- a/media/jni/audioeffect/android_media_AudioEffect.cpp +++ b/media/jni/audioeffect/android_media_AudioEffect.cpp @@ -455,9 +455,8 @@ static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz) env->SetIntField(thiz, fields.fidJniData, 0); } - static jint -android_media_AudioEffect_native_enable(JNIEnv *env, jobject thiz) +android_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled) { // retrieve the AudioEffect object AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField( @@ -469,29 +468,11 @@ android_media_AudioEffect_native_enable(JNIEnv *env, jobject thiz) return AUDIOEFFECT_ERROR_NO_INIT; } - return translateError(lpAudioEffect->enable()); + return translateError(lpAudioEffect->setEnabled(enabled)); } - -static jint -android_media_AudioEffect_native_disable(JNIEnv *env, jobject thiz) -{ - // retrieve the AudioEffect object - AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField( - thiz, fields.fidNativeAudioEffect); - - if (lpAudioEffect == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve AudioEffect pointer for disable()"); - return AUDIOEFFECT_ERROR_NO_INIT; - } - - return translateError(lpAudioEffect->disable()); -} - - static jboolean -android_media_AudioEffect_native_getEnable(JNIEnv *env, jobject thiz) +android_media_AudioEffect_native_getEnabled(JNIEnv *env, jobject thiz) { // retrieve the AudioEffect object AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField( @@ -503,7 +484,7 @@ android_media_AudioEffect_native_getEnable(JNIEnv *env, jobject thiz) return false; } - return (jboolean)lpAudioEffect->isEnabled(); + return (jboolean)lpAudioEffect->getEnabled(); } @@ -516,7 +497,7 @@ android_media_AudioEffect_native_hasControl(JNIEnv *env, jobject thiz) if (lpAudioEffect == NULL) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve AudioEffect pointer for getEnabled()"); + "Unable to retrieve AudioEffect pointer for hasControl()"); return false; } @@ -817,9 +798,8 @@ static JNINativeMethod gMethods[] = { (void *)android_media_AudioEffect_native_setup}, {"native_finalize", "()V", (void *)android_media_AudioEffect_native_finalize}, {"native_release", "()V", (void *)android_media_AudioEffect_native_release}, - {"native_enable", "()I", (void *)android_media_AudioEffect_native_enable}, - {"native_disable", "()I", (void *)android_media_AudioEffect_native_disable}, - {"native_getEnable", "()Z", (void *)android_media_AudioEffect_native_getEnable}, + {"native_setEnabled", "(Z)I", (void *)android_media_AudioEffect_native_setEnabled}, + {"native_getEnabled", "()Z", (void *)android_media_AudioEffect_native_getEnabled}, {"native_hasControl", "()Z", (void *)android_media_AudioEffect_native_hasControl}, {"native_setParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_setParameter}, {"native_getParameter", "(I[B[I[B)I", (void *)android_media_AudioEffect_native_getParameter}, @@ -830,6 +810,8 @@ static JNINativeMethod gMethods[] = { // ---------------------------------------------------------------------------- +extern int register_android_media_visualizer(JNIEnv *env); + int register_android_media_AudioEffect(JNIEnv *env) { return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); @@ -852,6 +834,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) goto bail; } + if (register_android_media_visualizer(env) < 0) { + LOGE("ERROR: Visualizer native registration failed\n"); + goto bail; + } + /* success -- return valid version number */ result = JNI_VERSION_1_4; diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp new file mode 100644 index 000000000000..31119f86e3a1 --- /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 b5f1d42ab401..54e87f3bdd9e 100644 --- a/media/libeffects/Android.mk +++ b/media/libeffects/Android.mk @@ -94,3 +94,33 @@ LOCAL_PRELINK_MODULE := false include $(BUILD_SHARED_LIBRARY) endif + + +# Visualizer library +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + EffectVisualizer.cpp + +LOCAL_CFLAGS+= -O2 + +LOCAL_SHARED_LIBRARIES := \ + libcutils + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE:= libvisualizer + +ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) +LOCAL_LDLIBS += -ldl +endif + +ifneq ($(TARGET_SIMULATOR),true) +LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_C_INCLUDES := \ + $(call include-path-for, graphics corecg) + +LOCAL_PRELINK_MODULE := false + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libeffects/EffectVisualizer.cpp b/media/libeffects/EffectVisualizer.cpp new file mode 100644 index 000000000000..f27e296cb8c4 --- /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 de9e51da4cbf..977e6be04776 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -30,7 +30,8 @@ LOCAL_SRC_FILES:= \ MediaProfiles.cpp \ IEffect.cpp \ IEffectClient.cpp \ - AudioEffect.cpp + AudioEffect.cpp \ + Visualizer.cpp LOCAL_SHARED_LIBRARIES := \ libui libcutils libutils libbinder libsonivox libicuuc libexpat libsurfaceflinger_client libcamera_client diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp index 4afa2dc62a69..783249d82a4e 100644 --- a/media/libmedia/AudioEffect.cpp +++ b/media/libmedia/AudioEffect.cpp @@ -171,7 +171,7 @@ AudioEffect::~AudioEffect() LOGV("Destructor %p", this); if (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS) { - disable(); + setEnabled(false); if (mIEffect != NULL) { mIEffect->disconnect(); mIEffect->asBinder()->unlinkToDeath(mIEffectClient); @@ -196,36 +196,28 @@ effect_descriptor_t AudioEffect::descriptor() const return mDescriptor; } -bool AudioEffect::isEnabled() const +bool AudioEffect::getEnabled() const { return (mEnabled != 0); } -status_t AudioEffect::enable() +status_t AudioEffect::setEnabled(bool enabled) { if (mStatus != NO_ERROR) { return INVALID_OPERATION; } - LOGV("enable %p", this); - if (android_atomic_or(1, &mEnabled) == 0) { - return mIEffect->enable(); - } - - return INVALID_OPERATION; -} - -status_t AudioEffect::disable() -{ - if (mStatus != NO_ERROR) { - return INVALID_OPERATION; - } - LOGV("disable %p", this); - - if (android_atomic_and(~1, &mEnabled) == 1) { - return mIEffect->disable(); + if (enabled) { + LOGV("enable %p", this); + if (android_atomic_or(1, &mEnabled) == 0) { + return mIEffect->enable(); + } + } else { + LOGV("disable %p", this); + if (android_atomic_and(~1, &mEnabled) == 1) { + return mIEffect->disable(); + } } - return INVALID_OPERATION; } @@ -349,7 +341,7 @@ void AudioEffect::controlStatusChanged(bool controlGranted) void AudioEffect::enableStatusChanged(bool enabled) { - LOGV("enableStatusChanged %p enabled %d", this, enabled); + LOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf); if (mStatus == ALREADY_EXISTS) { mEnabled = enabled; if (mCbf) { diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index 1ae222ea4638..4abfa75b3bfc 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -35,8 +35,7 @@ enum { DECODE_FD, CREATE_MEDIA_RECORDER, CREATE_METADATA_RETRIEVER, - GET_OMX, - SNOOP + GET_OMX }; class BpMediaPlayerService: public BpInterface<IMediaPlayerService> @@ -134,14 +133,6 @@ public: return interface_cast<IMemory>(reply.readStrongBinder()); } - virtual sp<IMemory> snoop() - { - Parcel data, reply; - data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); - remote()->transact(SNOOP, data, &reply); - return interface_cast<IMemory>(reply.readStrongBinder()); - } - virtual sp<IOMX> getOMX() { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); @@ -221,12 +212,6 @@ status_t BnMediaPlayerService::onTransact( reply->writeStrongBinder(player->asBinder()); return NO_ERROR; } break; - case SNOOP: { - CHECK_INTERFACE(IMediaPlayerService, data, reply); - sp<IMemory> snooped_audio = snoop(); - reply->writeStrongBinder(snooped_audio->asBinder()); - return NO_ERROR; - } break; case CREATE_MEDIA_RECORDER: { CHECK_INTERFACE(IMediaPlayerService, data, reply); pid_t pid = data.readInt32(); diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp new file mode 100644 index 000000000000..47e96e523372 --- /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 d5a3c133e816..b43f75f31e90 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -658,61 +658,4 @@ void MediaPlayer::died() } -extern "C" { -#define FLOATING_POINT 1 -#include "fftwrap.h" -} - -static void *ffttable = NULL; - -// peeks at the audio data and fills 'data' with the requested kind -// (currently kind=0 returns mono 16 bit PCM data, and kind=1 returns -// 256 point FFT data). Return value is number of samples returned, -// which may be 0. -/*static*/ int MediaPlayer::snoop(short* data, int len, int kind) { - - sp<IMemory> p; - const sp<IMediaPlayerService>& service = getMediaPlayerService(); - if (service != 0) { - // Take a peek at the waveform. The returned data consists of 16 bit mono PCM data. - p = service->snoop(); - - if (p == NULL) { - return 0; - } - - if (kind == 0) { // return waveform data - int plen = p->size(); - len *= 2; // number of shorts -> number of bytes - short *src = (short*) p->pointer(); - if (plen > len) { - plen = len; - } - memcpy(data, src, plen); - return plen / sizeof(short); // return number of samples - } else if (kind == 1) { - // TODO: use a more efficient FFT - // Right now this uses the speex library, which is compiled to do a float FFT - if (!ffttable) ffttable = spx_fft_init(512); - short *usrc = (short*) p->pointer(); - float fsrc[512]; - for (int i=0;i<512;i++) - fsrc[i] = usrc[i]; - float fdst[512]; - spx_fft_float(ffttable, fsrc, fdst); - if (len > 512) { - len = 512; - } - len /= 2; // only half the output data is valid - for (int i=0; i < len; i++) - data[i] = fdst[i]; - return len; - } - - } else { - LOGE("Unable to locate media service"); - } - return 0; -} - }; // namespace android diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 82d5c1466014..4872047cd8a0 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -1265,98 +1265,6 @@ Exit: return mem; } -/* - * Avert your eyes, ugly hack ahead. - * The following is to support music visualizations. - */ - -static const int NUMVIZBUF = 32; -static const int VIZBUFFRAMES = 1024; -static const int BUFTIMEMSEC = NUMVIZBUF * VIZBUFFRAMES * 1000 / 44100; -static const int TOTALBUFTIMEMSEC = NUMVIZBUF * BUFTIMEMSEC; - -static bool gotMem = false; -static sp<MemoryHeapBase> heap; -static sp<MemoryBase> mem[NUMVIZBUF]; -static uint64_t endTime; -static uint64_t lastReadTime; -static uint64_t lastWriteTime; -static int writeIdx = 0; - -static void allocVizBufs() { - if (!gotMem) { - heap = new MemoryHeapBase(NUMVIZBUF * VIZBUFFRAMES * 2, 0, "snooper"); - for (int i=0;i<NUMVIZBUF;i++) { - mem[i] = new MemoryBase(heap, VIZBUFFRAMES * 2 * i, VIZBUFFRAMES * 2); - } - endTime = 0; - gotMem = true; - } -} - - -/* - * Get a buffer of audio data that is about to be played. - * We don't synchronize this because in practice the writer - * is ahead of the reader, and even if we did happen to catch - * a buffer while it's being written, it's just a visualization, - * so no harm done. - */ -static sp<MemoryBase> getVizBuffer() { - - allocVizBufs(); - - lastReadTime = uptimeMillis(); - - // if there is no recent buffer (yet), just return empty handed - if (lastWriteTime + TOTALBUFTIMEMSEC < lastReadTime) { - //LOGI("@@@@ no audio data to look at yet: %d + %d < %d", (int)lastWriteTime, TOTALBUFTIMEMSEC, (int)lastReadTime); - return NULL; - } - - int timedelta = endTime - lastReadTime; - if (timedelta < 0) timedelta = 0; - int framedelta = timedelta * 44100 / 1000; - int headIdx = (writeIdx - framedelta) / VIZBUFFRAMES - 1; - while (headIdx < 0) { - headIdx += NUMVIZBUF; - } - return mem[headIdx]; -} - -// Append the data to the vizualization buffer -static void makeVizBuffers(const char *data, int len, uint64_t time) { - - allocVizBufs(); - - uint64_t startTime = time; - const int frameSize = 4; // 16 bit stereo sample is 4 bytes - int offset = writeIdx; - int maxoff = heap->getSize() / 2; // in shorts - short *base = (short*)heap->getBase(); - short *src = (short*)data; - while (len > 0) { - - // Degrade quality by mixing to mono and clearing the lowest 3 bits. - // This should still be good enough for a visualization - base[offset++] = ((int(src[0]) + int(src[1])) >> 1) & ~0x7; - src += 2; - len -= frameSize; - if (offset >= maxoff) { - offset = 0; - } - } - writeIdx = offset; - endTime = time + (len / frameSize) / 44; - //LOGI("@@@ stored buffers from %d to %d", uint32_t(startTime), uint32_t(time)); -} - -sp<IMemory> MediaPlayerService::snoop() -{ - sp<MemoryBase> mem = getVizBuffer(); - return mem; -} - #undef LOG_TAG #define LOG_TAG "AudioSink" @@ -1371,7 +1279,6 @@ MediaPlayerService::AudioOutput::AudioOutput(int sessionId) mRightVolume = 1.0; mLatency = 0; mMsecsPerFrame = 0; - mNumFramesWritten = 0; setMinBufferCount(); } @@ -1516,30 +1423,9 @@ void MediaPlayerService::AudioOutput::start() if (mTrack) { mTrack->setVolume(mLeftVolume, mRightVolume); mTrack->start(); - mTrack->getPosition(&mNumFramesWritten); } } -void MediaPlayerService::AudioOutput::snoopWrite(const void* buffer, size_t size) { - // Only make visualization buffers if anyone recently requested visualization data - uint64_t now = uptimeMillis(); - if (lastReadTime + TOTALBUFTIMEMSEC >= now) { - // Based on the current play counter, the number of frames written and - // the current real time we can calculate the approximate real start - // time of the buffer we're about to write. - uint32_t pos; - mTrack->getPosition(&pos); - - // we're writing ahead by this many frames: - int ahead = mNumFramesWritten - pos; - //LOGI("@@@ written: %d, playpos: %d, latency: %d", mNumFramesWritten, pos, mTrack->latency()); - // which is this many milliseconds, assuming 44100 Hz: - ahead /= 44; - - makeVizBuffers((const char*)buffer, size, now + ahead + mTrack->latency()); - lastWriteTime = now; - } -} ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size) @@ -1548,9 +1434,7 @@ ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size) //LOGV("write(%p, %u)", buffer, size); if (mTrack) { - snoopWrite(buffer, size); ssize_t ret = mTrack->write(buffer, size); - mNumFramesWritten += ret / 4; // assume 16 bit stereo return ret; } return NO_INIT; @@ -1560,7 +1444,6 @@ void MediaPlayerService::AudioOutput::stop() { LOGV("stop"); if (mTrack) mTrack->stop(); - lastWriteTime = 0; } void MediaPlayerService::AudioOutput::flush() @@ -1573,7 +1456,6 @@ void MediaPlayerService::AudioOutput::pause() { LOGV("pause"); if (mTrack) mTrack->pause(); - lastWriteTime = 0; } void MediaPlayerService::AudioOutput::close() @@ -1609,9 +1491,6 @@ void MediaPlayerService::AudioOutput::CallbackWrapper( buffer->size = actualSize; - if (actualSize > 0) { - me->snoopWrite(buffer->raw, actualSize); - } } #undef LOG_TAG diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 60b91c6907d0..39f525e7c212 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -113,9 +113,6 @@ class MediaPlayerService : public BnMediaPlayerService static bool mIsOnEmulator; static int mMinBufferCount; // 12 for emulator; otherwise 4 - public: // visualization hack support - uint32_t mNumFramesWritten; - void snoopWrite(const void*, size_t); }; class AudioCache : public MediaPlayerBase::AudioSink @@ -191,7 +188,6 @@ public: virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length, int audioSessionId); virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); - virtual sp<IMemory> snoop(); virtual sp<IOMX> getOMX(); virtual status_t dump(int fd, const Vector<String16>& args); |