diff options
Diffstat (limited to 'media/java')
| -rw-r--r-- | media/java/android/media/AudioFormat.java | 4 | ||||
| -rw-r--r-- | media/java/android/media/AudioManager.java | 242 | ||||
| -rw-r--r-- | media/java/android/media/AudioRecord.java | 134 | ||||
| -rw-r--r-- | media/java/android/media/AudioService.java | 414 | ||||
| -rw-r--r-- | media/java/android/media/AudioSystem.java | 20 | ||||
| -rw-r--r-- | media/java/android/media/AudioTrack.java | 476 | ||||
| -rw-r--r-- | media/java/android/media/JetPlayer.java | 134 | ||||
| -rw-r--r-- | media/java/android/media/MediaFile.java | 6 | ||||
| -rw-r--r-- | media/java/android/media/MediaPlayer.java | 6 | ||||
| -rw-r--r-- | media/java/android/media/MediaRecorder.java | 6 | ||||
| -rw-r--r-- | media/java/android/media/MediaScanner.java | 10 |
11 files changed, 869 insertions, 583 deletions
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index c857e17..0732b61 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -18,9 +18,9 @@ package android.media; /** * The AudioFormat class is used to access a number of audio format and - * channel configuration constants. + * channel configuration constants. They are for instance used + * in __link AudioTrack} and __link AudioRecord}. * - * {@hide Pending API council review} */ public class AudioFormat { diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 52cf69f..2978774 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -49,37 +49,60 @@ public class AudioManager { private static boolean localLOGV = DEBUG || android.util.Config.LOGV; /** + * Broadcast intent, a hint for applications that audio is about to become + * 'noisy' due to a change in audio outputs. For example, this intent may + * be sent when a wired headset is unplugged, or when an A2DP audio + * sink is disconnected, and the audio system is about to automatically + * switch audio route to the speaker. Applications that are controlling + * audio streams may consider pausing, reducing volume or some other action + * on receipt of this intent so as not to surprise the user with audio + * from the speaker. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_AUDIO_BECOMING_NOISY = "android.media.AUDIO_BECOMING_NOISY"; + + /** * Sticky broadcast intent action indicating that the ringer mode has * changed. Includes the new ringer mode. - * + * * @see #EXTRA_RINGER_MODE */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String RINGER_MODE_CHANGED_ACTION = "android.media.RINGER_MODE_CHANGED"; - + /** * The new ringer mode. - * + * * @see #RINGER_MODE_CHANGED_ACTION * @see #RINGER_MODE_NORMAL * @see #RINGER_MODE_SILENT * @see #RINGER_MODE_VIBRATE */ public static final String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE"; - + /** * Broadcast intent action indicating that the vibrate setting has * changed. Includes the vibrate type and its new setting. - * + * * @see #EXTRA_VIBRATE_TYPE * @see #EXTRA_VIBRATE_SETTING */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String VIBRATE_SETTING_CHANGED_ACTION = "android.media.VIBRATE_SETTING_CHANGED"; - + + /** + * @hide Broadcast intent when the volume for a particular stream type changes. + * Includes the stream and the new volume + * + * @see #EXTRA_VOLUME_STREAM_TYPE + * @see #EXTRA_VOLUME_STREAM_VALUE + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION"; + /** * The new vibrate setting for a particular type. - * + * * @see #VIBRATE_SETTING_CHANGED_ACTION * @see #EXTRA_VIBRATE_TYPE * @see #VIBRATE_SETTING_ON @@ -87,16 +110,27 @@ public class AudioManager { * @see #VIBRATE_SETTING_ONLY_SILENT */ public static final String EXTRA_VIBRATE_SETTING = "android.media.EXTRA_VIBRATE_SETTING"; - + /** * The vibrate type whose setting has changed. - * + * * @see #VIBRATE_SETTING_CHANGED_ACTION * @see #VIBRATE_TYPE_NOTIFICATION * @see #VIBRATE_TYPE_RINGER */ public static final String EXTRA_VIBRATE_TYPE = "android.media.EXTRA_VIBRATE_TYPE"; - + + /** + * @hide The stream type for the volume changed intent. + */ + public static final String EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM_TYPE"; + + /** + * @hide The volume associated with the stream for the volume changed intent. + */ + public static final String EXTRA_VOLUME_STREAM_VALUE = + "android.media.EXTRA_VOLUME_STREAM_VALUE"; + /** The audio stream for phone calls */ public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL; /** The audio stream for system sounds */ @@ -109,6 +143,8 @@ public class AudioManager { public static final int STREAM_ALARM = AudioSystem.STREAM_ALARM; /** The audio stream for notifications */ public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION; + /** @hide The audio stream for phone calls when connected to bluetooth */ + public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO; /** Number of audio streams */ /** * @deprecated Use AudioSystem.getNumStreamTypes() instead @@ -123,9 +159,10 @@ public class AudioManager { 8, // STREAM_RING 16, // STREAM_MUSIC 8, // STREAM_ALARM - 8 // STREAM_NOTIFICATION - }; - + 8, // STREAM_NOTIFICATION + 15, // STREAM_BLUETOOTH_SCO + }; + /** @hide Default volume index values for audio streams */ public static final int[] DEFAULT_STREAM_VOLUME = new int[] { 4, // STREAM_VOICE_CALL @@ -133,12 +170,13 @@ public class AudioManager { 5, // STREAM_RING 11, // STREAM_MUSIC 6, // STREAM_ALARM - 5 // STREAM_NOTIFICATION - }; - + 5, // STREAM_NOTIFICATION + 7 // STREAM_BLUETOOTH_SCO + }; + /** * Increase the ringer volume. - * + * * @see #adjustVolume(int, int) * @see #adjustStreamVolume(int, int, int) */ @@ -146,7 +184,7 @@ public class AudioManager { /** * Decrease the ringer volume. - * + * * @see #adjustVolume(int, int) * @see #adjustStreamVolume(int, int, int) */ @@ -155,17 +193,17 @@ public class AudioManager { /** * Maintain the previous ringer volume. This may be useful when needing to * show the volume toast without actually modifying the volume. - * + * * @see #adjustVolume(int, int) * @see #adjustStreamVolume(int, int, int) */ public static final int ADJUST_SAME = 0; // Flags should be powers of 2! - + /** * Show a toast containing the current volume. - * + * * @see #adjustStreamVolume(int, int, int) * @see #adjustVolume(int, int) * @see #setStreamVolume(int, int, int) @@ -183,12 +221,12 @@ public class AudioManager { * mode (for example, the ring stream type). If this flag is included, this * behavior will be present regardless of the stream type being affected by * the ringer mode. - * + * * @see #adjustVolume(int, int) * @see #adjustStreamVolume(int, int, int) */ public static final int FLAG_ALLOW_RINGER_MODES = 1 << 1; - + /** * Whether to play a sound when changing the volume. * <p> @@ -197,28 +235,28 @@ public class AudioManager { * in some cases (for example, the decided stream type is not * {@link AudioManager#STREAM_RING}, or the volume is being adjusted * downward). - * + * * @see #adjustStreamVolume(int, int, int) * @see #adjustVolume(int, int) * @see #setStreamVolume(int, int, int) */ public static final int FLAG_PLAY_SOUND = 1 << 2; - + /** * Removes any sounds/vibrate that may be in the queue, or are playing (related to * changing volume). */ public static final int FLAG_REMOVE_SOUND_AND_VIBRATE = 1 << 3; - + /** * Whether to vibrate if going into the vibrate ringer mode. */ public static final int FLAG_VIBRATE = 1 << 4; - + /** * Ringer mode that will be silent and will not vibrate. (This overrides the * vibrate setting.) - * + * * @see #setRingerMode(int) * @see #getRingerMode() */ @@ -228,7 +266,7 @@ public class AudioManager { * Ringer mode that will be silent and will vibrate. (This will cause the * phone ringer to always vibrate, but the notification vibrate to only * vibrate if set.) - * + * * @see #setRingerMode(int) * @see #getRingerMode() */ @@ -238,7 +276,7 @@ public class AudioManager { * Ringer mode that may be audible and may vibrate. It will be audible if * the volume before changing out of this mode was audible. It will vibrate * if the vibrate setting is on. - * + * * @see #setRingerMode(int) * @see #getRingerMode() */ @@ -246,53 +284,53 @@ public class AudioManager { /** * Vibrate type that corresponds to the ringer. - * + * * @see #setVibrateSetting(int, int) * @see #getVibrateSetting(int) * @see #shouldVibrate(int) */ public static final int VIBRATE_TYPE_RINGER = 0; - + /** * Vibrate type that corresponds to notifications. - * + * * @see #setVibrateSetting(int, int) * @see #getVibrateSetting(int) * @see #shouldVibrate(int) */ public static final int VIBRATE_TYPE_NOTIFICATION = 1; - + /** * Vibrate setting that suggests to never vibrate. - * + * * @see #setVibrateSetting(int, int) * @see #getVibrateSetting(int) */ public static final int VIBRATE_SETTING_OFF = 0; - + /** * Vibrate setting that suggests to vibrate when possible. - * + * * @see #setVibrateSetting(int, int) * @see #getVibrateSetting(int) */ public static final int VIBRATE_SETTING_ON = 1; - + /** * Vibrate setting that suggests to only vibrate when in the vibrate ringer * mode. - * + * * @see #setVibrateSetting(int, int) * @see #getVibrateSetting(int) */ public static final int VIBRATE_SETTING_ONLY_SILENT = 2; - + /** * Suggests using the default stream type. This may not be used in all * places a stream type is needed. */ public static final int USE_DEFAULT_STREAM_TYPE = Integer.MIN_VALUE; - + private static IAudioService sService; /** @@ -315,8 +353,8 @@ public class AudioManager { /** * Adjusts the volume of a particular stream by one step in a direction. - * - * @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL}, + * + * @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL}, * {@link #STREAM_SYSTEM}, {@link #STREAM_RING}, {@link #STREAM_MUSIC} or * {@link #STREAM_ALARM} * @param direction The direction to adjust the volume. One of @@ -340,7 +378,7 @@ public class AudioManager { * active, it will have the highest priority regardless of if the in-call * screen is showing. Another example, if music is playing in the background * and a call is not active, the music stream will be adjusted. - * + * * @param direction The direction to adjust the volume. One of * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or * {@link #ADJUST_SAME}. @@ -361,7 +399,7 @@ public class AudioManager { /** * Adjusts the volume of the most relevant stream, or the given fallback * stream. - * + * * @param direction The direction to adjust the volume. One of * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or * {@link #ADJUST_SAME}. @@ -380,10 +418,10 @@ public class AudioManager { Log.e(TAG, "Dead object in adjustVolume", e); } } - + /** * Returns the current ringtone mode. - * + * * @return The current ringtone mode, one of {@link #RINGER_MODE_NORMAL}, * {@link #RINGER_MODE_SILENT}, or {@link #RINGER_MODE_VIBRATE}. * @see #setRingerMode(int) @@ -400,7 +438,7 @@ public class AudioManager { /** * Returns the maximum volume index for a particular stream. - * + * * @param streamType The stream type whose maximum volume index is returned. * @return The maximum valid volume index for the stream. * @see #getStreamVolume(int) @@ -417,7 +455,7 @@ public class AudioManager { /** * Returns the current volume index for a particular stream. - * + * * @param streamType The stream type whose volume index is returned. * @return The current volume index for the stream. * @see #getStreamMaxVolume(int) @@ -439,7 +477,7 @@ public class AudioManager { * Silent mode will mute the volume and will not vibrate. Vibrate mode will * mute the volume and vibrate. Normal mode will be audible and may vibrate * according to user settings. - * + * * @param ringerMode The ringer mode, one of {@link #RINGER_MODE_NORMAL}, * {@link #RINGER_MODE_SILENT}, or {@link #RINGER_MODE_VIBRATE}. * @see #getRingerMode() @@ -455,7 +493,7 @@ public class AudioManager { /** * Sets the volume index for a particular stream. - * + * * @param streamType The stream whose volume index should be set. * @param index The volume index to set. See * {@link #getStreamMaxVolume(int)} for the largest valid value. @@ -471,7 +509,7 @@ public class AudioManager { Log.e(TAG, "Dead object in setStreamVolume", e); } } - + /** * Solo or unsolo a particular stream. All other streams are muted. * <p> @@ -479,13 +517,13 @@ public class AudioManager { * with an active solo request on a stream dies, all streams that were muted * because of this request will be unmuted automatically. * <p> - * The solo requests for a given stream are cumulative: the AudioManager + * The solo requests for a given stream are cumulative: the AudioManager * can receive several solo requests from one or more clients and the stream * will be unsoloed only when the same number of unsolo requests are received. * <p> - * For a better user experience, applications MUST unsolo a soloed stream + * For a better user experience, applications MUST unsolo a soloed stream * in onPause() and solo is again in onResume() if appropriate. - * + * * @param streamType The stream to be soloed/unsoloed. * @param state The required solo state: true for solo ON, false for solo OFF */ @@ -497,7 +535,7 @@ public class AudioManager { Log.e(TAG, "Dead object in setStreamSolo", e); } } - + /** * Mute or unmute an audio stream. * <p> @@ -505,13 +543,13 @@ public class AudioManager { * with an active mute request on a stream dies, this stream will be unmuted * automatically. * <p> - * The mute requests for a given stream are cumulative: the AudioManager + * The mute requests for a given stream are cumulative: the AudioManager * can receive several mute requests from one or more clients and the stream * will be unmuted only when the same number of unmute requests are received. * <p> - * For a better user experience, applications MUST unmute a muted stream + * For a better user experience, applications MUST unmute a muted stream * in onPause() and mute is again in onResume() if appropriate. - * + * * @param streamType The stream to be muted/unmuted. * @param state The required mute state: true for mute ON, false for mute OFF */ @@ -532,7 +570,7 @@ public class AudioManager { * vibrate. The notification manager will not vibrate if the policy doesn't * allow it, so the client should always set a vibrate pattern and let the * notification manager control whether or not to actually vibrate. - * + * * @param vibrateType The type of vibrate. One of * {@link #VIBRATE_TYPE_NOTIFICATION} or * {@link #VIBRATE_TYPE_RINGER}. @@ -550,13 +588,13 @@ public class AudioManager { return false; } } - + /** * Returns whether the user's vibrate setting for a vibrate type. * <p> * This shouldn't be needed by most clients that want to vibrate, instead * see {@link #shouldVibrate(int)}. - * + * * @param vibrateType The type of vibrate. One of * {@link #VIBRATE_TYPE_NOTIFICATION} or * {@link #VIBRATE_TYPE_RINGER}. @@ -578,7 +616,7 @@ public class AudioManager { /** * Sets the setting for when the vibrate type should vibrate. - * + * * @param vibrateType The type of vibrate. One of * {@link #VIBRATE_TYPE_NOTIFICATION} or * {@link #VIBRATE_TYPE_RINGER}. @@ -597,11 +635,11 @@ public class AudioManager { Log.e(TAG, "Dead object in setVibrateSetting", e); } } - + /** * Sets the speakerphone on or off. * - * @param on set <var>true</var> to turn on speakerphone; + * @param on set <var>true</var> to turn on speakerphone; * <var>false</var> to turn it off */ public void setSpeakerphoneOn(boolean on){ @@ -620,17 +658,17 @@ public class AudioManager { /** * Sets audio routing to the Bluetooth headset on or off. * - * @param on set <var>true</var> to route SCO (voice) audio to/from Bluetooth + * @param on set <var>true</var> to route SCO (voice) audio to/from Bluetooth * headset; <var>false</var> to route audio to/from phone earpiece */ public void setBluetoothScoOn(boolean on){ // Don't disable A2DP when turning off SCO. // A2DP does not affect in-call routing. - setRouting(MODE_RINGTONE, + setRouting(MODE_RINGTONE, on ? ROUTE_BLUETOOTH_SCO: ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP); - setRouting(MODE_NORMAL, + setRouting(MODE_NORMAL, on ? ROUTE_BLUETOOTH_SCO: ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP); - setRouting(MODE_IN_CALL, + setRouting(MODE_IN_CALL, on ? ROUTE_BLUETOOTH_SCO: ROUTE_EARPIECE, ROUTE_ALL); } @@ -647,15 +685,15 @@ public class AudioManager { /** * Sets A2DP audio routing to the Bluetooth headset on or off. * - * @param on set <var>true</var> to route A2DP audio to/from Bluetooth + * @param on set <var>true</var> to route A2DP audio to/from Bluetooth * headset; <var>false</var> disable A2DP audio */ public void setBluetoothA2dpOn(boolean on){ // the audio flinger chooses A2DP as a higher priority, // so there is no need to disable other routes. - setRouting(MODE_RINGTONE, + setRouting(MODE_RINGTONE, on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP); - setRouting(MODE_NORMAL, + setRouting(MODE_NORMAL, on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP); } @@ -672,7 +710,7 @@ public class AudioManager { /** * Sets audio routing to the wired headset on or off. * - * @param on set <var>true</var> to route audio to/from wired + * @param on set <var>true</var> to route audio to/from wired * headset; <var>false</var> disable wired headset audio * @hide */ @@ -701,7 +739,7 @@ public class AudioManager { /** * Sets the microphone mute on or off. * - * @param on set <var>true</var> to mute the microphone; + * @param on set <var>true</var> to mute the microphone; * <var>false</var> to turn mute off */ public void setMicrophoneMute(boolean on){ @@ -762,57 +800,57 @@ public class AudioManager { /* modes for setMode/getMode/setRoute/getRoute */ /** - * Audio harware modes. - */ + * Audio harware modes. + */ /** - * Invalid audio mode. - */ + * Invalid audio mode. + */ public static final int MODE_INVALID = AudioSystem.MODE_INVALID; /** * Current audio mode. Used to apply audio routing to current mode. - */ + */ public static final int MODE_CURRENT = AudioSystem.MODE_CURRENT; /** * Normal audio mode: not ringing and no call established. - */ + */ public static final int MODE_NORMAL = AudioSystem.MODE_NORMAL; /** * Ringing audio mode. An incoming is being signaled. - */ + */ public static final int MODE_RINGTONE = AudioSystem.MODE_RINGTONE; /** * In call audio mode. A call is established. - */ + */ public static final int MODE_IN_CALL = AudioSystem.MODE_IN_CALL; /* Routing bits for setRouting/getRouting API */ /** * Routing audio output to earpiece - */ + */ public static final int ROUTE_EARPIECE = AudioSystem.ROUTE_EARPIECE; /** * Routing audio output to spaker - */ + */ public static final int ROUTE_SPEAKER = AudioSystem.ROUTE_SPEAKER; /** * @deprecated use {@link #ROUTE_BLUETOOTH_SCO} - */ + */ @Deprecated public static final int ROUTE_BLUETOOTH = AudioSystem.ROUTE_BLUETOOTH_SCO; /** * Routing audio output to bluetooth SCO - */ + */ public static final int ROUTE_BLUETOOTH_SCO = AudioSystem.ROUTE_BLUETOOTH_SCO; /** * Routing audio output to headset - */ + */ public static final int ROUTE_HEADSET = AudioSystem.ROUTE_HEADSET; /** * Routing audio output to bluetooth A2DP - */ + */ public static final int ROUTE_BLUETOOTH_A2DP = AudioSystem.ROUTE_BLUETOOTH_A2DP; /** * Used for mask parameter of {@link #setRouting(int,int,int)}. - */ + */ public static final int ROUTE_ALL = AudioSystem.ROUTE_ALL; /** @@ -892,33 +930,33 @@ public class AudioManager { /** * Keyboard and direction pad click sound * @see #playSoundEffect(int) - */ + */ public static final int FX_KEY_CLICK = 0; /** * Focuse has moved up * @see #playSoundEffect(int) - */ + */ public static final int FX_FOCUS_NAVIGATION_UP = 1; /** * Focuse has moved down * @see #playSoundEffect(int) - */ + */ public static final int FX_FOCUS_NAVIGATION_DOWN = 2; /** * Focuse has moved left * @see #playSoundEffect(int) - */ + */ public static final int FX_FOCUS_NAVIGATION_LEFT = 3; /** * Focuse has moved right * @see #playSoundEffect(int) - */ + */ public static final int FX_FOCUS_NAVIGATION_RIGHT = 4; /** - * @hide Number of sound effects - */ + * @hide Number of sound effects + */ public static final int NUM_SOUND_EFFECTS = 5; - + /** * Plays a sound effect (Key clicks, lid open/close...) * @param effectType The type of sound effect. One of @@ -951,10 +989,10 @@ public class AudioManager { private boolean querySoundEffectsEnabled() { return Settings.System.getInt(mContext.getContentResolver(), Settings.System.SOUND_EFFECTS_ENABLED, 0) != 0; } - - + + /** - * Load Sound effects. + * Load Sound effects. * This method must be called when sound effects are enabled. */ public void loadSoundEffects() { @@ -965,9 +1003,9 @@ public class AudioManager { Log.e(TAG, "Dead object in loadSoundEffects"+e); } } - + /** - * Unload Sound effects. + * Unload Sound effects. * This method can be called to free some memory when * sound effects are disabled. */ diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 7912003b..1314ba1 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -34,11 +34,14 @@ import android.util.Log; * to record audio from the audio input hardware of the platform. This is * achieved by "pulling" (reading) the data from the AudioRecord object. The * application is responsible for polling the AudioRecord object in time using one of - * the following three methods: {@link #read(byte[], int)}, {@link #read(short[], int)} + * the following three methods: {@link #read(byte[],int, int)}, {@link #read(short[], int, int)} * or {@link #read(ByteBuffer, int)}. The choice of which method to use will be based * on the audio data storage format that is the most convenient for the user of AudioRecord. - * - * {@hide Pending API council review} + * <p>Upon creation, an AudioRecord object initializes its associated audio buffer that it will + * fill with the new audio data. The size of this buffer, specified during the construction, + * determines how long an AudioRecord can record before "over-running" data that has not + * been read yet. Data should be from the audio hardware in chunks of sizes inferior to + * the total recording buffer size. */ public class AudioRecord { @@ -82,22 +85,22 @@ public class AudioRecord */ public static final int ERROR_INVALID_OPERATION = -3; - private static final int AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT = -4; - private static final int AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT = -5; - private static final int AUDIORECORD_ERROR_SETUP_INVALIDFORMAT = -6; - private static final int AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE = -7; - private static final int AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED = -8; + private static final int AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT = -16; + private static final int AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT = -17; + private static final int AUDIORECORD_ERROR_SETUP_INVALIDFORMAT = -18; + private static final int AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE = -19; + private static final int AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED = -20; // Events: // to keep in sync with frameworks/base/include/media/AudioRecord.h /** * Event id for when the recording head has reached a previously set marker. */ - protected static final int EVENT_MARKER = 2; + private static final int NATIVE_EVENT_MARKER = 2; /** * Event id for when the previously set update period has passed during recording. */ - protected static final int EVENT_NEW_POS = 3; + private static final int NATIVE_EVENT_NEW_POS = 3; private final static String TAG = "AudioRecord-Java"; @@ -130,63 +133,63 @@ public class AudioRecord /** * The audio data sampling rate in Hz. */ - protected int mSampleRate = 22050; + private int mSampleRate = 22050; /** * The number of input audio channels (1 is mono, 2 is stereo) */ - protected int mChannelCount = 1; + private int mChannelCount = 1; /** * The current audio channel configuration */ - protected int mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; + private int mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; /** * The encoding of the audio samples. - * @see #AudioFormat.ENCODING_PCM_8BIT - * @see #AudioFormat.ENCODING_PCM_16BIT + * @see AudioFormat#ENCODING_PCM_8BIT + * @see AudioFormat#ENCODING_PCM_16BIT */ - protected int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; + private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; /** * Where the audio data is recorded from. */ - protected int mRecordSource = MediaRecorder.AudioSource.DEFAULT; + private int mRecordSource = MediaRecorder.AudioSource.DEFAULT; /** * Indicates the state of the AudioRecord instance. */ - protected int mState = STATE_UNINITIALIZED; + private int mState = STATE_UNINITIALIZED; /** * Indicates the recording state of the AudioRecord instance. */ - protected int mRecordingState = RECORDSTATE_STOPPED; + private int mRecordingState = RECORDSTATE_STOPPED; /** * Lock to make sure mRecordingState updates are reflecting the actual state of the object. */ - protected Object mRecordingStateLock = new Object(); + private Object mRecordingStateLock = new Object(); /** * The listener the AudioRecord notifies when a previously set marker is reached. * @see #setMarkerReachedListener(OnMarkerReachedListener) */ - protected OnMarkerReachedListener mMarkerListener = null; + private OnMarkerReachedListener mMarkerListener = null; /** * Lock to protect marker listener updates against event notifications */ - protected final Object mMarkerListenerLock = new Object(); + private final Object mMarkerListenerLock = new Object(); /** * The listener the AudioRecord notifies periodically during recording. * @see #setPeriodicNotificationListener(OnPeriodicNotificationListener) */ - protected OnPeriodicNotificationListener mPeriodicListener = null; + private OnPeriodicNotificationListener mPeriodicListener = null; /** * Lock to protect periodic listener updates against event notifications */ - protected final Object mPeriodicListenerLock = new Object(); + private final Object mPeriodicListenerLock = new Object(); /** * Handler for events coming from the native code */ - protected NativeEventHandler mNativeEventHandler = null; + private NativeEventHandler mNativeEventHandler = null; /** * Size of the native audio buffer. */ - protected int mNativeBufferSizeInBytes = 0; + private int mNativeBufferSizeInBytes = 0; //--------------------------------------------------------- @@ -194,30 +197,30 @@ public class AudioRecord //-------------------- /** * Class constructor. - * @param audioSource the recording source. See {@link MediaRecorder.AudioSource.DEFAULT} - * and {@link MediaRecorder.AudioSource.MIC}. + * @param audioSource the recording source. See {@link MediaRecorder.AudioSource} for + * recording source definitions. * @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but * not limited to) 44100, 22050 and 11025. * @param channelConfig describes the configuration of the audio channels. - * See {@link AudioFormat.CHANNEL_CONFIGURATION_MONO} and - * {@link AudioFormat.CHANNEL_CONFIGURATION_STEREO} + * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and + * {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO} * @param audioFormat the format in which the audio data is represented. - * See {@link AudioFormat.ENCODING_PCM_16BIT} and - * {@link AudioFormat.ENCODING_PCM_8BIT} + * See {@link AudioFormat#ENCODING_PCM_16BIT} and + * {@link AudioFormat#ENCODING_PCM_8BIT} * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written * to during the recording. New audio data can be read from this buffer in smaller chunks * than this size. * @throws java.lang.IllegalArgumentException */ public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, - int blockSizeInBytes) + int bufferSizeInBytes) throws IllegalArgumentException { mState = STATE_UNINITIALIZED; mRecordingState = RECORDSTATE_STOPPED; audioParamCheck(audioSource, sampleRateInHz, channelConfig, audioFormat); - audioBuffSizeCheck(blockSizeInBytes); + audioBuffSizeCheck(bufferSizeInBytes); // native initialization //TODO: update native initialization when information about hardware init failure @@ -367,8 +370,8 @@ public class AudioRecord } /** - * Returns the configured audio data format. See {@link #AudioFormat.ENCODING_PCM_16BIT} - * and {@link #AudioFormat.ENCODING_PCM_8BIT}. + * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT} + * and {@link AudioFormat#ENCODING_PCM_8BIT}. */ public int getAudioFormat() { return mAudioFormat; @@ -376,8 +379,8 @@ public class AudioRecord /** * Returns the configured channel configuration. - * See {@link #AudioFormat.CHANNEL_CONFIGURATION_MONO} - * and {@link #AudioFormat.CHANNEL_CONFIGURATION_STEREO}. + * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} + * and {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO}. */ public int getChannelConfiguration() { return mChannelConfiguration; @@ -395,8 +398,8 @@ public class AudioRecord * AudioRecord instance has been created to check if it was initialized * properly. This ensures that the appropriate hardware resources have been * acquired. - * @see AudioRecord.STATE_INITIALIZED - * @see AudioRecord.STATE_UNINITIALIZED + * @see AudioRecord#STATE_INITIALIZED + * @see AudioRecord#STATE_UNINITIALIZED */ public int getState() { return mState; @@ -404,8 +407,8 @@ public class AudioRecord /** * Returns the recording state of the AudioRecord instance. - * @see AudioRecord.RECORDSTATE_STOPPED - * @see AudioRecord.RECORDSTATE_RECORDING + * @see AudioRecord#RECORDSTATE_STOPPED + * @see AudioRecord#RECORDSTATE_RECORDING */ public int getRecordingState() { return mRecordingState; @@ -472,16 +475,15 @@ public class AudioRecord //-------------------- /** * Reads audio data from the audio hardware for recording into a buffer. - * @throws IllegalStateException * @param audioData the array to which the recorded audio data is written. * @param offsetInBytes index in audioData from which the data is written. * @param sizeInBytes the number of requested bytes. - * @return the number of bytes that were read. This will not exceed sizeInBytes + * @return the number of bytes that were read or -1 if the object wasn't properly + * initialized. The number of bytes will not exceed sizeInBytes. */ - public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) - throws IllegalStateException { + public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) { if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("read() called on uninitialized AudioRecord.")); + return -1; } return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes); @@ -490,16 +492,15 @@ public class AudioRecord /** * Reads audio data from the audio hardware for recording into a buffer. - * @throws IllegalStateException * @param audioData the array to which the recorded audio data is written. * @param offsetInShorts index in audioData from which the data is written. * @param sizeInShorts the number of requested shorts. - * @return the number of shorts that were read. This will not exceed sizeInShorts + * @return the number of shorts that were read. or -1 if the object wasn't properly + * initialized. The number of shorts will not exceed sizeInShorts */ - public int read(short[] audioData, int offsetInShorts, int sizeInShorts) - throws IllegalStateException { + public int read(short[] audioData, int offsetInShorts, int sizeInShorts) { if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("read() called on uninitialized AudioRecord.")); + return -1; } return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts); @@ -509,15 +510,14 @@ public class AudioRecord /** * Reads audio data from the audio hardware for recording into a direct buffer. If this buffer * is not a direct buffer, this method will always return 0. - * @throws IllegalStateException * @param audioBuffer the direct buffer to which the recorded audio data is written. * @param sizeInBytes the number of requested bytes. - * @return the number of bytes that were read. This will not exceed sizeInBytes. + * @return the number of bytes that were read or -1 if the object wasn't properly + * initialized. The number of bytes will not exceed sizeInBytes. */ - public int read(ByteBuffer audioBuffer, int sizeInBytes) - throws IllegalStateException { + public int read(ByteBuffer audioBuffer, int sizeInBytes) { if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("read() called on uninitialized AudioRecord.")); + return -1; } return native_read_in_direct_buffer(audioBuffer, sizeInBytes); @@ -590,7 +590,7 @@ public class AudioRecord * Called on the listener to notify it that the previously set marker has been reached * by the recording head. */ - void onMarkerReached(AudioRecord track); + void onMarkerReached(AudioRecord recorder); } @@ -603,7 +603,7 @@ public class AudioRecord * Called on the listener to periodically notify it that the recording head has reached * a multiple of the notification period. */ - void onPeriodicNotification(AudioRecord track); + void onPeriodicNotification(AudioRecord recorder); } @@ -628,14 +628,14 @@ public class AudioRecord return; } switch(msg.what) { - case EVENT_MARKER: + case NATIVE_EVENT_MARKER: synchronized (mMarkerListenerLock) { if (mAudioRecord.mMarkerListener != null) { mAudioRecord.mMarkerListener.onMarkerReached(mAudioRecord); } } break; - case EVENT_NEW_POS: + case NATIVE_EVENT_NEW_POS: synchronized (mPeriodicListenerLock) { if (mAudioRecord.mPeriodicListener != null) { mAudioRecord.mPeriodicListener.onPeriodicNotification(mAudioRecord); @@ -643,7 +643,7 @@ public class AudioRecord } break; default: - Log.e(TAG, "[ android.media.AudioTrack.NativeEventHandler ] " + + Log.e(TAG, "[ android.media.AudioRecord.NativeEventHandler ] " + "Unknown event type: " + msg.what); break; } @@ -658,14 +658,14 @@ public class AudioRecord private static void postEventFromNative(Object audiorecord_ref, int what, int arg1, int arg2, Object obj) { //logd("Event posted from the native side: event="+ what + " args="+ arg1+" "+arg2); - AudioRecord track = (AudioRecord)((WeakReference)audiorecord_ref).get(); - if (track == null) { + AudioRecord recorder = (AudioRecord)((WeakReference)audiorecord_ref).get(); + if (recorder == null) { return; } - if (track.mNativeEventHandler != null) { - Message m = track.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); - track.mNativeEventHandler.sendMessage(m); + if (recorder.mNativeEventHandler != null) { + Message m = recorder.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); + recorder.mNativeEventHandler.sendMessage(m); } } diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index b39e7bb..f3d8957 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -16,8 +16,7 @@ package android.media; -import com.android.internal.telephony.ITelephony; - +import android.app.ActivityManagerNative; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -32,11 +31,13 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; -import android.provider.Settings.System; import android.provider.Settings; +import android.provider.Settings.System; import android.util.Log; import android.view.VolumePanel; +import com.android.internal.telephony.ITelephony; + import java.io.IOException; import java.util.ArrayList; @@ -50,19 +51,19 @@ import java.util.ArrayList; * volume and later persist to the database. Similarly, setting the ringer mode * will update the state and broadcast a change and in a separate thread later * persist the ringer mode. - * + * * @hide */ public class AudioService extends IAudioService.Stub { - + private static final String TAG = "AudioService"; /** How long to delay before persisting a change in volume/ringer mode. */ private static final int PERSIST_DELAY = 3000; - + private Context mContext; private ContentResolver mContentResolver; - + /** The UI */ private VolumePanel mVolumePanel; @@ -75,7 +76,7 @@ public class AudioService extends IAudioService.Stub { private static final int SENDMSG_NOOP = 1; /** If the msg is already queued, queue this one and leave the old. */ private static final int SENDMSG_QUEUE = 2; - + // AudioHandler message.whats private static final int MSG_SET_SYSTEM_VOLUME = 0; private static final int MSG_PERSIST_VOLUME = 1; @@ -91,18 +92,18 @@ public class AudioService extends IAudioService.Stub { private AudioHandler mAudioHandler; /** @see VolumeStreamState */ private VolumeStreamState[] mStreamStates; - + private boolean mMicMute; private int mMode; private int[] mRoutes = new int[AudioSystem.NUM_MODES]; private Object mSettingsLock = new Object(); private boolean mMediaServerOk; - + private SoundPool mSoundPool; private Object mSoundEffectsLock = new Object(); private static final int NUM_SOUNDPOOL_CHANNELS = 4; private static final float SOUND_EFFECT_VOLUME = 1.0f; - + /* Sound effect file names */ private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/"; private static final String[] SOUND_EFFECT_FILES = new String[] { @@ -119,7 +120,7 @@ public class AudioService extends IAudioService.Stub { {0, -1}, // FX_FOCUS_NAVIGATION_LEFT {0, -1} // FX_FOCUS_NAVIGATION_RIGHT }; - + private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() { public void onError(int error) { switch (error) { @@ -145,7 +146,7 @@ public class AudioService extends IAudioService.Stub { * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL}, * {@link AudioManager#RINGER_MODE_SILENT}, or * {@link AudioManager#RINGER_MODE_VIBRATE}. - */ + */ private int mRingerMode; /** @see System#MODE_RINGER_STREAMS_AFFECTED */ @@ -153,7 +154,7 @@ public class AudioService extends IAudioService.Stub { /** @see System#MUTE_STREAMS_AFFECTED */ private int mMuteAffectedStreams; - + /** * Has multiple bits per vibrate type to indicate the type's vibrate * setting. See {@link #setVibrateSetting(int, int)}. @@ -162,23 +163,23 @@ public class AudioService extends IAudioService.Stub { * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}. */ private int mVibrateSetting; - + /////////////////////////////////////////////////////////////////////////// // Construction /////////////////////////////////////////////////////////////////////////// - + /** @hide */ public AudioService(Context context) { mContext = context; mContentResolver = context.getContentResolver(); mVolumePanel = new VolumePanel(context, this); - + createAudioSystemThread(); createStreamStates(); readPersistedSettings(); readAudioSettings(); mMediaServerOk = true; - AudioSystem.setErrorCallback(mAudioSystemCallback); + AudioSystem.setErrorCallback(mAudioSystemCallback); if (Settings.System.getInt(mContentResolver, Settings.System.SOUND_EFFECTS_ENABLED, 0) == 1) { loadSoundEffects(); } @@ -189,8 +190,8 @@ public class AudioService extends IAudioService.Stub { mAudioSystemThread.start(); waitForAudioHandlerCreation(); } - - /** Waits for the volume handler to be created by the other thread. */ + + /** Waits for the volume handler to be created by the other thread. */ private void waitForAudioHandlerCreation() { synchronized(this) { while (mAudioHandler == null) { @@ -203,37 +204,51 @@ public class AudioService extends IAudioService.Stub { } } } - + private void createStreamStates() { - final int[] volumeLevelsPhone = createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_VOICE_CALL]); - final int[] volumeLevelsCoarse = createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_SYSTEM]); - final int[] volumeLevelsFine = createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]); - + final int[] volumeLevelsPhone = + createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_VOICE_CALL]); + final int[] volumeLevelsCoarse = + createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_SYSTEM]); + final int[] volumeLevelsFine = + createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]); + final int[] volumeLevelsBtPhone = + createVolumeLevels(0, + AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_BLUETOOTH_SCO]); + int numStreamTypes = AudioSystem.getNumStreamTypes(); VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes]; - + for (int i = 0; i < numStreamTypes; i++) { final int[] levels; - + switch (i) { - + case AudioSystem.STREAM_MUSIC: levels = volumeLevelsFine; break; - + case AudioSystem.STREAM_VOICE_CALL: levels = volumeLevelsPhone; break; - + + case AudioSystem.STREAM_BLUETOOTH_SCO: + levels = volumeLevelsBtPhone; + break; + default: levels = volumeLevelsCoarse; break; } - - streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[i], i, levels); + + if (i == AudioSystem.STREAM_BLUETOOTH_SCO) { + streams[i] = new VolumeStreamState(AudioManager.DEFAULT_STREAM_VOLUME[i], i,levels); + } else { + streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[i], i, levels); + } } } - + private static int[] createVolumeLevels(int offset, int numlevels) { double curve = 1.0f; // 1.4f int [] volumes = new int[numlevels + offset]; @@ -249,7 +264,7 @@ public class AudioService extends IAudioService.Stub { } return volumes; } - + private void readPersistedSettings() { final ContentResolver cr = mContentResolver; @@ -260,19 +275,19 @@ public class AudioService extends IAudioService.Stub { mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0); mMuteAffectedStreams = System.getInt(cr, - System.MUTE_STREAMS_AFFECTED, + System.MUTE_STREAMS_AFFECTED, ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM))); - + // Each stream will read its own persisted settings - + // Broadcast the sticky intent broadcastRingerMode(); - + // Broadcast vibrate settings broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER); broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION); } - + private void readAudioSettings() { synchronized (mSettingsLock) { mMicMute = AudioSystem.isMicrophoneMuted(); @@ -282,7 +297,7 @@ public class AudioService extends IAudioService.Stub { } } } - + private void applyAudioSettings() { synchronized (mSettingsLock) { AudioSystem.muteMicrophone(mMicMute); @@ -291,46 +306,46 @@ public class AudioService extends IAudioService.Stub { AudioSystem.setRouting(mode, mRoutes[mode], AudioSystem.ROUTE_ALL); } } - } - + } + /////////////////////////////////////////////////////////////////////////// // IPC methods /////////////////////////////////////////////////////////////////////////// - + /** @see AudioManager#adjustVolume(int, int) */ public void adjustVolume(int direction, int flags) { adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags); } - + /** @see AudioManager#adjustVolume(int, int, int) */ public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) { int streamType = getActiveStreamType(suggestedStreamType); - + // Don't play sound on other streams if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) { flags &= ~AudioManager.FLAG_PLAY_SOUND; } - + adjustStreamVolume(streamType, direction, flags); } - + /** @see AudioManager#adjustStreamVolume(int, int, int) */ public void adjustStreamVolume(int streamType, int direction, int flags) { ensureValidDirection(direction); ensureValidStreamType(streamType); - + boolean notificationsUseRingVolume = Settings.System.getInt(mContentResolver, Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1) == 1; if (notificationsUseRingVolume && streamType == AudioManager.STREAM_NOTIFICATION) { // Redirect the volume change to the ring stream streamType = AudioManager.STREAM_RING; } - + VolumeStreamState streamState = mStreamStates[streamType]; - final int oldIndex = streamState.mIndex; + final int oldIndex = streamState.mIndex; boolean adjustVolume = true; - + // If either the client forces allowing ringer modes for this adjustment, // or the stream type is one that is affected by ringer modes if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0 @@ -339,21 +354,21 @@ public class AudioService extends IAudioService.Stub { // it does, it will handle adjusting the volome, so we won't below adjustVolume = checkForRingerModeChange(oldIndex, direction); } - + if (adjustVolume && streamState.adjustIndex(direction)) { - + boolean alsoUpdateNotificationVolume = notificationsUseRingVolume && streamType == AudioManager.STREAM_RING; if (alsoUpdateNotificationVolume) { mStreamStates[AudioManager.STREAM_NOTIFICATION].adjustIndex(direction); } - + // Post message to set system volume (it in turn will post a message // to persist). Do not change volume if stream is muted. if (streamState.muteCount() == 0) { sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0, streamState, 0); - + if (alsoUpdateNotificationVolume) { sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, AudioManager.STREAM_NOTIFICATION, SENDMSG_NOOP, 0, 0, mStreamStates[AudioManager.STREAM_NOTIFICATION], 0); @@ -363,12 +378,44 @@ public class AudioService extends IAudioService.Stub { // UI mVolumePanel.postVolumeChanged(streamType, flags); + // Broadcast Intent + sendVolumeUpdate(streamType); } /** @see AudioManager#setStreamVolume(int, int, int) */ public void setStreamVolume(int streamType, int index, int flags) { ensureValidStreamType(streamType); - + syncRingerAndNotificationStreamVolume(streamType, index, false); + + setStreamVolumeInt(streamType, index, false); + + // UI, etc. + mVolumePanel.postVolumeChanged(streamType, flags); + // Broadcast Intent + sendVolumeUpdate(streamType); + } + + private void sendVolumeUpdate(int streamType) { + Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION); + intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType); + intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, getStreamVolume(streamType)); + + // Currently, sending the intent only when the stream is BLUETOOTH_SCO + if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) { + mContext.sendBroadcast(intent); + } + } + + /** + * Sync the STREAM_RING and STREAM_NOTIFICATION volumes if mandated by the + * value in Settings. + * + * @param streamType Type of the stream + * @param index Volume index for the stream + * @param force If true, set the volume even if the current and desired + * volume as same + */ + private void syncRingerAndNotificationStreamVolume(int streamType, int index, boolean force) { boolean notificationsUseRingVolume = Settings.System.getInt(mContentResolver, Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1) == 1; if (notificationsUseRingVolume) { @@ -378,23 +425,24 @@ public class AudioService extends IAudioService.Stub { } if (streamType == AudioManager.STREAM_RING) { // One-off to sync notification volume to ringer volume - setStreamVolumeInt(AudioManager.STREAM_NOTIFICATION, index); + setStreamVolumeInt(AudioManager.STREAM_NOTIFICATION, index, force); } } - - setStreamVolumeInt(streamType, index); - - // UI, etc. - mVolumePanel.postVolumeChanged(streamType, flags); } + /** * Sets the stream state's index, and posts a message to set system volume. * This will not call out to the UI. Assumes a valid stream type. + * + * @param streamType Type of the stream + * @param index Desired volume index of the stream + * @param force If true, set the volume even if the desired volume is same + * as the current volume. */ - private void setStreamVolumeInt(int streamType, int index) { + private void setStreamVolumeInt(int streamType, int index, boolean force) { VolumeStreamState streamState = mStreamStates[streamType]; - if (streamState.setIndex(index)) { + if (streamState.setIndex(index) || force) { // Post message to set system volume (it in turn will post a message // to persist). Do not change volume if stream is muted. if (streamState.muteCount() == 0) { @@ -403,7 +451,7 @@ public class AudioService extends IAudioService.Stub { } } } - + /** @see AudioManager#setStreamSolo(int, boolean) */ public void setStreamSolo(int streamType, boolean state, IBinder cb) { for (int stream = 0; stream < mStreamStates.length; stream++) { @@ -412,7 +460,7 @@ public class AudioService extends IAudioService.Stub { mStreamStates[stream].mute(cb, state); } } - + /** @see AudioManager#setStreamMute(int, boolean) */ public void setStreamMute(int streamType, boolean state, IBinder cb) { if (isStreamAffectedByMute(streamType)) { @@ -441,32 +489,33 @@ public class AudioService extends IAudioService.Stub { public void setRingerMode(int ringerMode) { if (ringerMode != mRingerMode) { mRingerMode = ringerMode; - + // Adjust volumes via posting message int numStreamTypes = AudioSystem.getNumStreamTypes(); if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) { for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { if (!isStreamAffectedByRingerMode(streamType)) continue; // Bring back last audible volume - setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex); + setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex, + false); } } else { for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { if (!isStreamAffectedByRingerMode(streamType)) continue; // Either silent or vibrate, either way volume is 0 - setStreamVolumeInt(streamType, 0); + setStreamVolumeInt(streamType, 0, false); } } - + // Send sticky broadcast broadcastRingerMode(); - + // Post a persist ringer mode msg sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG, SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY); } } - + /** @see AudioManager#shouldVibrate(int) */ public boolean shouldVibrate(int vibrateType) { @@ -474,21 +523,21 @@ public class AudioService extends IAudioService.Stub { case AudioManager.VIBRATE_SETTING_ON: return mRingerMode != AudioManager.RINGER_MODE_SILENT; - + case AudioManager.VIBRATE_SETTING_ONLY_SILENT: return mRingerMode == AudioManager.RINGER_MODE_VIBRATE; - + case AudioManager.VIBRATE_SETTING_OFF: - // Phone ringer should always vibrate in vibrate mode + // Phone ringer should always vibrate in vibrate mode if (vibrateType == AudioManager.VIBRATE_TYPE_RINGER) { return mRingerMode == AudioManager.RINGER_MODE_VIBRATE; } - + default: return false; } } - + /** @see AudioManager#getVibrateSetting(int) */ public int getVibrateSetting(int vibrateType) { return (mVibrateSetting >> (vibrateType * 2)) & 3; @@ -498,10 +547,10 @@ public class AudioService extends IAudioService.Stub { public void setVibrateSetting(int vibrateType, int vibrateSetting) { mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting); - + // Broadcast change broadcastVibrateSetting(vibrateType); - + // Post message to set ringer mode (it in turn will post a message // to persist) sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0, @@ -518,13 +567,13 @@ public class AudioService extends IAudioService.Stub { // First clear the existing setting. Each vibrate type has two bits in // the value. Note '3' is '11' in binary. existingValue &= ~(3 << (vibrateType * 2)); - + // Set into the old value existingValue |= (vibrateSetting & 3) << (vibrateType * 2); - + return existingValue; } - + /** @see AudioManager#setMicrophoneMute(boolean) */ public void setMicrophoneMute(boolean on) { if (!checkAudioSettingsPermission("setMicrophoneMute()")) { @@ -534,15 +583,15 @@ public class AudioService extends IAudioService.Stub { if (on != mMicMute) { AudioSystem.muteMicrophone(on); mMicMute = on; - } + } } } - + /** @see AudioManager#isMicrophoneMute() */ public boolean isMicrophoneMute() { return mMicMute; } - + /** @see AudioManager#setMode(int) */ public void setMode(int mode) { if (!checkAudioSettingsPermission("setMode()")) { @@ -552,15 +601,19 @@ public class AudioService extends IAudioService.Stub { if (mode != mMode) { AudioSystem.setMode(mode); mMode = mode; - } + } + int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); + int index = mStreamStates[streamType].mIndex; + syncRingerAndNotificationStreamVolume(streamType, index, true); + setStreamVolumeInt(streamType, index, true); } } - + /** @see AudioManager#getMode() */ public int getMode() { return mMode; } - + /** @see AudioManager#setRouting(int, int, int) */ public void setRouting(int mode, int routes, int mask) { if (!checkAudioSettingsPermission("setRouting()")) { @@ -570,10 +623,14 @@ public class AudioService extends IAudioService.Stub { if ((mRoutes[mode] & mask) != (routes & mask)) { AudioSystem.setRouting(mode, routes, mask); mRoutes[mode] = (mRoutes[mode] & ~mask) | (routes & mask); - } + } + int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); + int index = mStreamStates[streamType].mIndex; + syncRingerAndNotificationStreamVolume(streamType, index, true); + setStreamVolumeInt(streamType, index, true); } } - + /** @see AudioManager#getRouting(int) */ public int getRouting(int mode) { return mRoutes[mode]; @@ -583,7 +640,7 @@ public class AudioService extends IAudioService.Stub { public boolean isMusicActive() { return AudioSystem.isMusicActive(); } - + /** @see AudioManager#setParameter(String, String) */ public void setParameter(String key, String value) { AudioSystem.setParameter(key, value); @@ -596,8 +653,8 @@ public class AudioService extends IAudioService.Stub { } /** - * Loads samples into the soundpool. - * This method must be called at when sound effects are enabled + * Loads samples into the soundpool. + * This method must be called at when sound effects are enabled */ public boolean loadSoundEffects() { synchronized (mSoundEffectsLock) { @@ -605,17 +662,17 @@ public class AudioService extends IAudioService.Stub { if (mSoundPool == null) { return false; } - /* - * poolId table: The value -1 in this table indicates that corresponding - * file (same index in SOUND_EFFECT_FILES[] has not been loaded. - * Once loaded, the value in poolId is the sample ID and the same + /* + * poolId table: The value -1 in this table indicates that corresponding + * file (same index in SOUND_EFFECT_FILES[] has not been loaded. + * Once loaded, the value in poolId is the sample ID and the same * sample can be reused for another effect using the same file. - */ + */ int[] poolId = new int[SOUND_EFFECT_FILES.length]; for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { poolId[fileIdx] = -1; } - /* + /* * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded. * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0: * this indicates we have a valid sample loaded for this effect. @@ -638,12 +695,12 @@ public class AudioService extends IAudioService.Stub { } } } - + return true; } /** - * Unloads samples from the sound pool. + * Unloads samples from the sound pool. * This method can be called to free some memory when * sound effects are disabled. */ @@ -665,12 +722,12 @@ public class AudioService extends IAudioService.Stub { mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]); SOUND_EFFECT_FILES_MAP[effect][1] = -1; poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1; - } + } } mSoundPool = null; } } - + /////////////////////////////////////////////////////////////////////////// // Internal methods /////////////////////////////////////////////////////////////////////////// @@ -683,7 +740,7 @@ public class AudioService extends IAudioService.Stub { private boolean checkForRingerModeChange(int oldIndex, int direction) { boolean adjustVolumeIndex = true; int newRingerMode = mRingerMode; - + if (mRingerMode == AudioManager.RINGER_MODE_NORMAL && oldIndex == 1 && direction == AudioManager.ADJUST_LOWER) { newRingerMode = AudioManager.RINGER_MODE_VIBRATE; @@ -697,7 +754,7 @@ public class AudioService extends IAudioService.Stub { && mRingerMode == AudioManager.RINGER_MODE_SILENT) { newRingerMode = AudioManager.RINGER_MODE_VIBRATE; } - + if (newRingerMode != mRingerMode) { setRingerMode(newRingerMode); @@ -708,18 +765,18 @@ public class AudioService extends IAudioService.Stub { */ adjustVolumeIndex = false; } - + return adjustVolumeIndex; } - + public boolean isStreamAffectedByRingerMode(int streamType) { - return (mRingerModeAffectedStreams & (1 << streamType)) != 0; + return (mRingerModeAffectedStreams & (1 << streamType)) != 0; } - + public boolean isStreamAffectedByMute(int streamType) { return (mMuteAffectedStreams & (1 << streamType)) != 0; } - + private void ensureValidDirection(int direction) { if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) { throw new IllegalArgumentException("Bad direction " + direction); @@ -736,13 +793,15 @@ public class AudioService extends IAudioService.Stub { boolean isOffhook = false; try { ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); - isOffhook = phone.isOffhook(); + if (phone != null) isOffhook = phone.isOffhook(); } catch (RemoteException e) { Log.w(TAG, "Couldn't connect to phone service", e); } - // TODO: applications can influence this - if (isOffhook) { + if ((getRouting(AudioSystem.MODE_IN_CALL) & AudioSystem.ROUTE_BLUETOOTH_SCO) != 0) { + // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); + return AudioSystem.STREAM_BLUETOOTH_SCO; + } else if (isOffhook) { // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL..."); return AudioSystem.STREAM_VOICE_CALL; } else if (AudioSystem.isMusicActive()) { @@ -756,29 +815,33 @@ public class AudioService extends IAudioService.Stub { return suggestedStreamType; } } - + private void broadcastRingerMode() { // Send sticky broadcast - Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION); - broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode); - long origCallerIdentityToken = Binder.clearCallingIdentity(); - mContext.sendStickyBroadcast(broadcast); - Binder.restoreCallingIdentity(origCallerIdentityToken); + if (ActivityManagerNative.isSystemReady()) { + Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION); + broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode); + long origCallerIdentityToken = Binder.clearCallingIdentity(); + mContext.sendStickyBroadcast(broadcast); + Binder.restoreCallingIdentity(origCallerIdentityToken); + } } - + private void broadcastVibrateSetting(int vibrateType) { // Send broadcast - Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); - broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType); - broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType)); - mContext.sendBroadcast(broadcast); + if (ActivityManagerNative.isSystemReady()) { + Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); + broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType); + broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType)); + mContext.sendBroadcast(broadcast); + } } - + // Message helper methods private static int getMsg(int baseMsg, int streamType) { - return (baseMsg & 0xffff) | streamType << 16; + return (baseMsg & 0xffff) | streamType << 16; } - + private static int getMsgBase(int msg) { return msg & 0xffff; } @@ -792,11 +855,11 @@ public class AudioService extends IAudioService.Stub { } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) { return; } - + handler .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay); } - + boolean checkAudioSettingsPermission(String method) { if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS") == PackageManager.PERMISSION_GRANTED) { @@ -809,29 +872,29 @@ public class AudioService extends IAudioService.Stub { return false; } - + /////////////////////////////////////////////////////////////////////////// // Inner classes /////////////////////////////////////////////////////////////////////////// - + public class VolumeStreamState { private final String mVolumeIndexSettingName; - private final String mLastAudibleVolumeIndexSettingName; + private final String mLastAudibleVolumeIndexSettingName; private final int mStreamType; - + private final int[] mVolumes; private int mIndex; private int mLastAudibleIndex; private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death - + private VolumeStreamState(String settingName, int streamType, int[] volumes) { - + mVolumeIndexSettingName = settingName; mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE; - + mStreamType = streamType; mVolumes = volumes; - + final ContentResolver cr = mContentResolver; mIndex = getValidIndex(Settings.System.getInt(cr, mVolumeIndexSettingName, AudioManager.DEFAULT_STREAM_VOLUME[streamType])); mLastAudibleIndex = getValidIndex(Settings.System.getInt(cr, @@ -841,14 +904,31 @@ public class AudioService extends IAudioService.Stub { mDeathHandlers = new ArrayList<VolumeDeathHandler>(); } + /** + * Constructor to be used when there is no setting associated with the VolumeStreamState. + * + * @param defaultVolume Default volume of the stream to use. + * @param streamType Type of the stream. + * @param volumes Volumes levels associated with this stream. + */ + private VolumeStreamState(int defaultVolume, int streamType, int[] volumes) { + mVolumeIndexSettingName = null; + mLastAudibleVolumeIndexSettingName = null; + mIndex = mLastAudibleIndex = defaultVolume; + mStreamType = streamType; + mVolumes = volumes; + AudioSystem.setVolume(mStreamType, defaultVolume); + mDeathHandlers = new ArrayList<VolumeDeathHandler>(); + } + public boolean adjustIndex(int deltaIndex) { return setIndex(mIndex + deltaIndex); } - + public boolean setIndex(int index) { int oldIndex = mIndex; mIndex = getValidIndex(index); - + if (oldIndex != mIndex) { if (mIndex > 0) { mLastAudibleIndex = mIndex; @@ -858,11 +938,11 @@ public class AudioService extends IAudioService.Stub { return false; } } - + public int getMaxIndex() { return mVolumes.length - 1; } - + public void mute(IBinder cb, boolean state) { VolumeDeathHandler handler = getDeathHandler(cb, state); if (handler == null) { @@ -871,17 +951,17 @@ public class AudioService extends IAudioService.Stub { } handler.mute(state); } - + private int getValidIndex(int index) { if (index < 0) { return 0; } else if (index >= mVolumes.length) { return mVolumes.length - 1; } - + return index; } - + private class VolumeDeathHandler implements IBinder.DeathRecipient { private IBinder mICallback; // To be notified of client's death private int mMuteCount; // Number of active mutes for this client @@ -924,7 +1004,7 @@ public class AudioService extends IAudioService.Stub { mDeathHandlers.remove(this); mICallback.unlinkToDeath(this, 0); if (muteCount() == 0) { - // If the stream is not mut any more, restore it's volume if + // If the stream is not mut any more, restore it's volume if // ringer mode allows it if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) { setIndex(mLastAudibleIndex); @@ -980,59 +1060,59 @@ public class AudioService extends IAudioService.Stub { } } } - + /** Thread that handles native AudioSystem control. */ private class AudioSystemThread extends Thread { AudioSystemThread() { super("AudioService"); } - + @Override public void run() { // Set this thread up so the handler will work on it Looper.prepare(); - + synchronized(AudioService.this) { mAudioHandler = new AudioHandler(); // Notify that the handler has been created AudioService.this.notify(); } - + // Listen for volume change requests that are set by VolumePanel Looper.loop(); } } - + /** Handles internal volume messages in separate volume thread. */ private class AudioHandler extends Handler { - + private void setSystemVolume(VolumeStreamState streamState) { - + // Adjust volume AudioSystem .setVolume(streamState.mStreamType, streamState.mVolumes[streamState.mIndex]); - + // Post a persist volume msg sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType, SENDMSG_REPLACE, 0, 0, streamState, PERSIST_DELAY); } - + private void persistVolume(VolumeStreamState streamState) { System.putInt(mContentResolver, streamState.mVolumeIndexSettingName, streamState.mIndex); System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName, streamState.mLastAudibleIndex); } - + private void persistRingerMode() { System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode); } - + private void persistVibrateSetting() { System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting); } - + private void playSoundEffect(int effectType) { synchronized (mSoundEffectsLock) { if (mSoundPool == null) { @@ -1084,36 +1164,36 @@ public class AudioService extends IAudioService.Stub { } } } - + @Override public void handleMessage(Message msg) { int baseMsgWhat = getMsgBase(msg.what); - + switch (baseMsgWhat) { - + case MSG_SET_SYSTEM_VOLUME: setSystemVolume((VolumeStreamState) msg.obj); break; - + case MSG_PERSIST_VOLUME: persistVolume((VolumeStreamState) msg.obj); break; - + case MSG_PERSIST_RINGER_MODE: persistRingerMode(); break; - + case MSG_PERSIST_VIBRATE_SETTING: persistVibrateSetting(); break; - + case MSG_MEDIA_SERVER_DIED: Log.e(TAG, "Media server died."); - // Force creation of new IAudioflinger interface + // Force creation of new IAudioflinger interface mMediaServerOk = false; AudioSystem.getMode(); break; - + case MSG_MEDIA_SERVER_STARTED: Log.e(TAG, "Media server started."); // Restore audio routing and stream volumes @@ -1139,5 +1219,5 @@ public class AudioService extends IAudioService.Stub { } } } - + } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 37100677..d0fa795 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -43,15 +43,17 @@ public class AudioSystem public static final int STREAM_ALARM = 4; /* The audio stream for notifications */ public static final int STREAM_NOTIFICATION = 5; + /* @hide The audio stream for phone calls when connected on bluetooth */ + public static final int STREAM_BLUETOOTH_SCO = 6; /** * @deprecated Use {@link #numStreamTypes() instead} */ public static final int NUM_STREAMS = 5; // Expose only the getter method publicly so we can change it in the future - private static final int NUM_STREAM_TYPES = 6; + private static final int NUM_STREAM_TYPES = 7; public static final int getNumStreamTypes() { return NUM_STREAM_TYPES; } - + /* max and min volume levels */ /* Maximum volume setting, for use with setVolume(int,int) */ public static final int MAX_VOLUME = 100; @@ -78,7 +80,7 @@ public class AudioSystem /* * Sets the microphone mute on or off. * - * param on set <var>true</var> to mute the microphone; + * param on set <var>true</var> to mute the microphone; * <var>false</var> to turn mute off * return command completion status see AUDIO_STATUS_OK, see AUDIO_STATUS_ERROR */ @@ -116,18 +118,18 @@ public class AudioSystem public static final int MODE_RINGTONE = 1; public static final int MODE_IN_CALL = 2; public static final int NUM_MODES = 3; - + /* Routing bits for setRouting/getRouting API */ public static final int ROUTE_EARPIECE = (1 << 0); public static final int ROUTE_SPEAKER = (1 << 1); - - /** @deprecated use {@link #ROUTE_BLUETOOTH_SCO} */ + + /** @deprecated use {@link #ROUTE_BLUETOOTH_SCO} */ @Deprecated public static final int ROUTE_BLUETOOTH = (1 << 2); public static final int ROUTE_BLUETOOTH_SCO = (1 << 2); public static final int ROUTE_HEADSET = (1 << 3); public static final int ROUTE_BLUETOOTH_A2DP = (1 << 4); - public static final int ROUTE_ALL = 0xFFFFFFFF; + public static final int ROUTE_ALL = 0xFFFFFFFF; /* * Sets the audio routing for a specified mode @@ -185,7 +187,7 @@ public class AudioSystem public static final int AUDIO_STATUS_ERROR = 1; /* Media server died. see ErrorCallback */ public static final int AUDIO_STATUS_SERVER_DIED = 100; - + private static ErrorCallback mErrorCallback; /* @@ -211,7 +213,7 @@ public class AudioSystem { mErrorCallback = cb; } - + private static void errorCallbackFromNative(int error) { if (mErrorCallback != null) { diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 2b7656f..9bb1df9 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -30,12 +30,31 @@ import android.util.Log; /** * The AudioTrack class manages and plays a single audio resource for Java applications. * It allows to stream PCM audio buffers to the audio hardware for playback. This is - * be achieved by "pushing" the data to the AudioTrack object using the - * {@link #write(byte[], int, int)} or {@link #write(short[], int, int)} method. - * During construction, an AudioTrack object can be initialized with a given buffer. - * This size determines how long an AudioTrack can play before running out of data. - * - * {@hide Pending API council review} + * achieved by "pushing" the data to the AudioTrack object using one of the + * {@link #write(byte[], int, int)} and {@link #write(short[], int, int)} methods. + * <p>An AudioTrack instance can operate under two modes: static of streaming.<br> + * The Streaming mode consists in continuously writing data to the AudioTrack, using one + * of the write() methods. These are blocking and return when the data has been transferred + * from the Java layer to the native layer, and is queued for playback. The streaming mode + * is most useful when playing blocks of audio data that for instance are: + * <ul> + * <li>too big to fit in memory because of the duration of the sound to play,</li> + * <li>too big to fit in memory because of the characteristics of the audio data + * (high sampling rate, bits per sample ...)</li> + * <li>chosen, received or generated as the audio keeps playing.</li> + * </ul> + * The static mode is to be chosen when dealing with short sounds that fit in memory and + * that need to be played with the smallest latency possible. Static mode AudioTrack instances can + * play the sound without the need to transfer the audio data from Java to the audio hardware + * each time the sound is to be played. The static mode will therefore be preferred for UI and + * game sounds that are played often, and with the smallest overhead possible. + * <p>Upon creation, an AudioTrack object initializes its associated audio buffer. + * The size of this buffer, specified during the construction, determines how long an AudioTrack + * can play before running out of data.<br> + * For an AudioTrack using the static mode, this size is the maximum size of the sound that can + * be played from it.<br> + * For the streaming mode, data will be written to the hardware in chunks of + * sizes inferior to the total buffer size. */ public class AudioTrack { @@ -46,42 +65,41 @@ public class AudioTrack private static final float VOLUME_MIN = 0.0f; /** Maximum value for a channel volume */ private static final float VOLUME_MAX = 1.0f; - + /** state of an AudioTrack this is stopped */ public static final int PLAYSTATE_STOPPED = 1; // matches SL_PLAYSTATE_STOPPED /** state of an AudioTrack this is paused */ public static final int PLAYSTATE_PAUSED = 2; // matches SL_PLAYSTATE_PAUSED /** state of an AudioTrack this is playing */ public static final int PLAYSTATE_PLAYING = 3; // matches SL_PLAYSTATE_PLAYING - - /** - * Creation mode where audio data is transferred from Java to the native layer + + /** + * Creation mode where audio data is transferred from Java to the native layer * only once before the audio starts playing. */ public static final int MODE_STATIC = 0; - /** - * Creation mode where audio data is streamed from Java to the native layer + /** + * Creation mode where audio data is streamed from Java to the native layer * as the audio is playing. */ public static final int MODE_STREAM = 1; - - /** - * State of an AudioTrack that was not successfully initialized upon creation + + /** + * State of an AudioTrack that was not successfully initialized upon creation */ public static final int STATE_UNINITIALIZED = 0; - /** + /** * State of an AudioTrack that is ready to be used. */ public static final int STATE_INITIALIZED = 1; /** - * State of a successfully initialized AudioTrack that uses static data, + * State of a successfully initialized AudioTrack that uses static data, * but that hasn't received that data yet. */ public static final int STATE_NO_STATIC_DATA = 2; - - // to keep in sync with libs/android_runtime/android_media_AudioTrack.cpp - // error codes + // Error codes: + // to keep in sync with frameworks/base/core/jni/android_media_AudioTrack.cpp /** * Denotes a successful operation. */ @@ -90,139 +108,145 @@ public class AudioTrack * Denotes a generic operation failure. */ public static final int ERROR = -1; - private static final int ERROR_NATIVESETUP_AUDIOSYSTEM = -2; - private static final int ERROR_NATIVESETUP_INVALIDCHANNELCOUNT = -3; - private static final int ERROR_NATIVESETUP_INVALIDFORMAT = -4; - private static final int ERROR_NATIVESETUP_INVALIDSTREAMTYPE = -5; - private static final int ERROR_NATIVESETUP_NATIVEINITFAILED = -6; /** * Denotes a failure due to the use of an invalid value. */ - public static final int ERROR_BAD_VALUE = -7; + public static final int ERROR_BAD_VALUE = -2; /** * Denotes a failure due to the improper use of a method. */ - public static final int ERROR_INVALID_OPERATION = -8; - // events + public static final int ERROR_INVALID_OPERATION = -3; + + private static final int ERROR_NATIVESETUP_AUDIOSYSTEM = -16; + private static final int ERROR_NATIVESETUP_INVALIDCHANNELCOUNT = -17; + private static final int ERROR_NATIVESETUP_INVALIDFORMAT = -18; + private static final int ERROR_NATIVESETUP_INVALIDSTREAMTYPE = -19; + private static final int ERROR_NATIVESETUP_NATIVEINITFAILED = -20; + + // Events: + // to keep in sync with frameworks/base/include/media/AudioTrack.h /** * Event id for when the playback head has reached a previously set marker. */ - protected static final int NATIVE_EVENT_MARKER = 3; + private static final int NATIVE_EVENT_MARKER = 3; /** * Event id for when the previously set update period has passed during playback. */ - protected static final int NATIVE_EVENT_NEW_POS = 4; - + private static final int NATIVE_EVENT_NEW_POS = 4; + private final static String TAG = "AudioTrack-Java"; - + //-------------------------------------------------------------------------- // Member variables //-------------------- /** * Indicates the state of the AudioTrack instance */ - protected int mState = STATE_UNINITIALIZED; + private int mState = STATE_UNINITIALIZED; /** * Indicates the play state of the AudioTrack instance */ - protected int mPlayState = PLAYSTATE_STOPPED; + private int mPlayState = PLAYSTATE_STOPPED; /** * Lock to make sure mPlayState updates are reflecting the actual state of the object. */ - protected final Object mPlayStateLock = new Object(); + private final Object mPlayStateLock = new Object(); /** * The listener the AudioTrack notifies previously set marker is reached. * @see #setMarkerReachedListener(OnMarkerReachedListener) */ - protected OnMarkerReachedListener mMarkerListener = null; + private OnMarkerReachedListener mMarkerListener = null; /** * Lock to protect marker listener updates against event notifications */ - protected final Object mMarkerListenerLock = new Object(); + private final Object mMarkerListenerLock = new Object(); /** * The listener the AudioTrack notifies periodically during playback. * @see #setPeriodicNotificationListener(OnPeriodicNotificationListener) */ - protected OnPeriodicNotificationListener mPeriodicListener = null; + private OnPeriodicNotificationListener mPeriodicListener = null; /** * Lock to protect periodic listener updates against event notifications */ - protected final Object mPeriodicListenerLock = new Object(); + private final Object mPeriodicListenerLock = new Object(); /** * Size of the native audio buffer. */ - protected int mNativeBufferSizeInBytes = 0; + private int mNativeBufferSizeInBytes = 0; /** * Handler for events coming from the native code */ - protected NativeEventHandler mNativeEventHandler = null; + private NativeEventHandler mNativeEventHandler = null; /** * The audio data sampling rate in Hz. */ - protected int mSampleRate = 22050; + private int mSampleRate = 22050; /** * The number of input audio channels (1 is mono, 2 is stereo) */ - protected int mChannelCount = 1; + private int mChannelCount = 1; /** * The type of the audio stream to play. See - * {@link AudioManager.STREAM_VOICE_CALL}, {@link AudioManager.STREAM_SYSTEM}, - * {@link AudioManager.STREAM_RING}, {@link AudioManager.STREAM_MUSIC} and - * {@link AudioManager.STREAM_ALARM} + * {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM}, + * {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC} and + * {@link AudioManager#STREAM_ALARM} */ - protected int mStreamType = AudioManager.STREAM_MUSIC; + private int mStreamType = AudioManager.STREAM_MUSIC; /** * The way audio is consumed by the hardware, streaming or static. */ - protected int mDataLoadMode = MODE_STREAM; + private int mDataLoadMode = MODE_STREAM; /** * The current audio channel configuration */ - protected int mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; + private int mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; /** * The encoding of the audio samples. - * @see #AudioFormat.ENCODING_PCM_8BIT - * @see #AudioFormat.ENCODING_PCM_16BIT + * @see AudioFormat#ENCODING_PCM_8BIT + * @see AudioFormat#ENCODING_PCM_16BIT */ - protected int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; + private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; //-------------------------------- // Used exclusively by native code //-------------------- - /** - * Accessed by native methods: provides access to C++ AudioTrack object + /** + * Accessed by native methods: provides access to C++ AudioTrack object */ @SuppressWarnings("unused") private int mNativeTrackInJavaObj; - /** + /** * Accessed by native methods: provides access to the JNI data (i.e. resources used by * the native AudioTrack object, but not stored in it). */ @SuppressWarnings("unused") private int mJniData; - - + + //-------------------------------------------------------------------------- // Constructor, Finalize //-------------------- /** * Class constructor. - * @param streamType the type of the audio stream. See - * {@link AudioSystem.STREAM_VOICE_CALL}, {@link AudioSystem.STREAM_SYSTEM}, - * {@link AudioSystem.STREAM_RING}, {@link AudioSystem.STREAM_MUSIC} and - * {@link AudioSystem.STREAM_ALARM} + * @param streamType the type of the audio stream. See + + * {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM}, + * {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC} and + * {@link AudioManager#STREAM_ALARM} * @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but * not limited to) 44100, 22050 and 11025. - * @param channelConfig describes the configuration of the audio channels. - * See {@link AudioFormat.CHANNEL_CONFIGURATION_MONO} and - * {@link AudioFormat.CHANNEL_CONFIGURATION_STEREO} - * @param audioFormat the format in which the audio data is represented. - * See {@link AudioFormat.ENCODING_PCM_16BIT} and - * {@link AudioFormat.ENCODING_PCM_8BIT} + * @param channelConfig describes the configuration of the audio channels. + + * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and + * {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO} + + * @param audioFormat the format in which the audio data is represented. + * See {@link AudioFormat#ENCODING_PCM_16BIT} and + * {@link AudioFormat#ENCODING_PCM_8BIT} * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is read - * from for playback. If using the AudioTrack in streaming mode, you can write data into + * from for playback. If using the AudioTrack in streaming mode, you can write data into * this buffer in smaller chunks than this size. If using the AudioTrack in static mode, * this is the maximum size of the sound that will be played for this instance. * @param mode streaming or static buffer. See {@link #MODE_STATIC} and {@link #MODE_STREAM} @@ -230,7 +254,7 @@ public class AudioTrack */ public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode) - throws IllegalArgumentException { + throws IllegalArgumentException { mState = STATE_UNINITIALIZED; audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat, mode); @@ -238,8 +262,8 @@ public class AudioTrack audioBuffSizeCheck(bufferSizeInBytes); // native initialization - int initResult = native_setup(new WeakReference<AudioTrack>(this), - mStreamType, mSampleRate, mChannelCount, mAudioFormat, + int initResult = native_setup(new WeakReference<AudioTrack>(this), + mStreamType, mSampleRate, mChannelCount, mAudioFormat, mNativeBufferSizeInBytes, mDataLoadMode); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); @@ -252,8 +276,8 @@ public class AudioTrack mState = STATE_INITIALIZED; } } - - + + // Convenience method for the constructor's parameter checks. // This is where constructor IllegalArgumentException-s are thrown // postconditions: @@ -262,25 +286,27 @@ public class AudioTrack // mAudioFormat is valid // mSampleRate is valid // mDataLoadMode is valid - private void audioParamCheck(int streamType, int sampleRateInHz, + private void audioParamCheck(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int mode) { - + //-------------- // stream type if( (streamType != AudioManager.STREAM_ALARM) && (streamType != AudioManager.STREAM_MUSIC) && (streamType != AudioManager.STREAM_RING) && (streamType != AudioManager.STREAM_SYSTEM) - && (streamType != AudioManager.STREAM_VOICE_CALL) && (streamType != AudioManager.STREAM_NOTIFICATION) ) { + && (streamType != AudioManager.STREAM_VOICE_CALL) + && (streamType != AudioManager.STREAM_NOTIFICATION) + && (streamType != AudioManager.STREAM_BLUETOOTH_SCO)) { throw (new IllegalArgumentException("Invalid stream type.")); } else { mStreamType = streamType; } - + //-------------- // sample rate if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) { throw (new IllegalArgumentException(sampleRateInHz + "Hz is not a supported sample rate.")); - } else { + } else { mSampleRate = sampleRateInHz; } @@ -314,10 +340,10 @@ public class AudioTrack break; default: mAudioFormat = AudioFormat.ENCODING_INVALID; - throw(new IllegalArgumentException("Unsupported sample encoding." + throw(new IllegalArgumentException("Unsupported sample encoding." + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT.")); } - + //-------------- // audio load mode if ( (mode != MODE_STREAM) && (mode != MODE_STATIC) ) { @@ -326,8 +352,8 @@ public class AudioTrack mDataLoadMode = mode; } } - - + + // Convenience method for the contructor's audio buffer size check. // preconditions: // mChannelCount is valid @@ -335,18 +361,18 @@ public class AudioTrack // postcondition: // mNativeBufferSizeInBytes is valid (multiple of frame size, positive) private void audioBuffSizeCheck(int audioBufferSize) { - // NB: this section is only valid with PCM data. + // NB: this section is only valid with PCM data. // To update when supporting compressed formats - int frameSizeInBytes = mChannelCount + int frameSizeInBytes = mChannelCount * (mAudioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2); if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) { throw (new IllegalArgumentException("Invalid audio buffer size.")); } - + mNativeBufferSizeInBytes = audioBufferSize; } - - + + // Convenience method for the creation of the native event handler // It is called only when a non-null event listener is set. // precondition: @@ -361,8 +387,8 @@ public class AudioTrack mNativeEventHandler = null; } } - - + + /** * Releases the native AudioTrack resources. */ @@ -377,7 +403,7 @@ public class AudioTrack @Override protected void finalize() { native_finalize(); - } + } //-------------------------------------------------------------------------- // Getters @@ -390,7 +416,7 @@ public class AudioTrack static public float getMinVolume() { return AudioTrack.VOLUME_MIN; } - + /** * Returns the maximum valid volume value. Volume values set above this one will * be clamped at this value. @@ -398,8 +424,8 @@ public class AudioTrack */ static public float getMaxVolume() { return AudioTrack.VOLUME_MAX; - } - + } + /** * Returns the configured audio data sample rate in Hz */ @@ -408,27 +434,28 @@ public class AudioTrack } /** - * Returns the configured audio data format. See {@link #AudioFormat.ENCODING_PCM_16BIT} - * and {@link #AudioFormat.ENCODING_PCM_8BIT}. + * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT} + * and {@link AudioFormat#ENCODING_PCM_8BIT}. */ public int getAudioFormat() { return mAudioFormat; } - + /** * Returns the type of audio stream this AudioTrack is configured for. - * Compare the result against {@link AudioManager.STREAM_VOICE_CALL}, - * {@link AudioManager.STREAM_SYSTEM}, {@link AudioManager.STREAM_RING}, - * {@link AudioManager.STREAM_MUSIC} or {@link AudioManager.STREAM_ALARM} + * Compare the result against {@link AudioManager#STREAM_VOICE_CALL}, + * {@link AudioManager#STREAM_SYSTEM}, {@link AudioManager#STREAM_RING}, + * {@link AudioManager#STREAM_MUSIC} or {@link AudioManager#STREAM_ALARM} */ public int getStreamType() { return mStreamType; } /** - * Returns the configured channel configuration. - * See {@link #AudioFormat.CHANNEL_CONFIGURATION_MONO} - * and {@link #AudioFormat.CHANNEL_CONFIGURATION_STEREO}. + * Returns the configured channel configuration. + + * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} + * and {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO}. */ public int getChannelConfiguration() { return mChannelConfiguration; @@ -443,19 +470,19 @@ public class AudioTrack /** * Returns the state of the AudioTrack instance. This is useful after the - * AudioTrack instance has been created to check if it was initialized + * AudioTrack instance has been created to check if it was initialized * properly. This ensures that the appropriate hardware resources have been * acquired. */ public int getState() { return mState; } - + /** * Returns the playback state of the AudioTrack instance. - * @see AudioTrack.PLAYSTATE_STOPPED - * @see AudioTrack.PLAYSTATE_PAUSED - * @see AudioTrack.PLAYSTATE_PLAYING + * @see #PLAYSTATE_STOPPED + * @see #PLAYSTATE_PAUSED + * @see #PLAYSTATE_PLAYING */ public int getPlayState() { return mPlayState; @@ -495,11 +522,49 @@ public class AudioTrack static public int getNativeOutputSampleRate() { return native_get_output_sample_rate(); } - + /** + * {@hide} + * Returns the minimum buffer size required for the successful creation of an AudioTrack + * object to be created in the {@link #MODE_STREAM} mode. + * @param sampleRateInHz the sample rate expressed in Hertz. + * @param channelConfig describes the configuration of the audio channels. + * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and + * {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO} + * @param audioFormat the format in which the audio data is represented. + * See {@link AudioFormat#ENCODING_PCM_16BIT} and + * {@link AudioFormat#ENCODING_PCM_8BIT} + * @return -1 if an invalid parameter was passed or if the implementation was unable to + * query the hardware for its output properties, or the minimum buffer size expressed + * in number of bytes. + */ + static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) { + int channelCount = 0; + switch(channelConfig) { + case AudioFormat.CHANNEL_CONFIGURATION_MONO: + channelCount = 1; + break; + case AudioFormat.CHANNEL_CONFIGURATION_STEREO: + channelCount = 2; + break; + default: + loge("getMinBufferSize(): Invalid channel configuration."); + return -1; + } + + if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT) + && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) { + loge("getMinBufferSize(): Invalid audio format."); + return -1; + } + + return native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat); + } + + //-------------------------------------------------------------------------- // Initialization / configuration - //-------------------- + //-------------------- /** * Sets the listener the AudioTrack notifies when a previously set marker is reached. * @param listener @@ -512,8 +577,8 @@ public class AudioTrack createNativeEventHandler(); } } - - + + /** * Sets the listener the AudioTrack notifies periodically during playback. * @param listener @@ -526,22 +591,20 @@ public class AudioTrack createNativeEventHandler(); } } - - - /** - * Sets the specified left/right output volume values on the AudioTrack. Values are clamped + + + /** + * Sets the specified left/right output volume values on the AudioTrack. Values are clamped * to the ({@link #getMinVolume()}, {@link #getMaxVolume()}) interval if outside this range. - * @param leftVolume output attenuation for the left channel. A value of 0.0f is silence, + * @param leftVolume output attenuation for the left channel. A value of 0.0f is silence, * a value of 1.0f is no attenuation. * @param rightVolume output attenuation for the right channel - * @return {@link #SUCCESS} - * @throws IllegalStateException + * @return error code or success, see {@link #SUCCESS}, + * {@link #ERROR_INVALID_OPERATION} */ - public int setStereoVolume(float leftVolume, float rightVolume) - throws IllegalStateException { + public int setStereoVolume(float leftVolume, float rightVolume) { if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("setStereoVolume() called on an "+ - "uninitialized AudioTrack.")); + return ERROR_INVALID_OPERATION; } // clamp the volumes @@ -559,11 +622,11 @@ public class AudioTrack } native_setVolume(leftVolume, rightVolume); - + return SUCCESS; } - - + + /** * Sets the playback sample rate for this track. This sets the sampling rate at which * the audio data will be consumed and played back, not the original sampling rate of the @@ -573,70 +636,88 @@ public class AudioTrack * sample rate (see {@link #getNativeOutputSampleRate()}). Use {@link #getSampleRate()} to * check the rate actually used in hardware after potential clamping. * @param sampleRateInHz - * @return {@link #SUCCESS} + * @return error code or success, see {@link #SUCCESS}, + * {@link #ERROR_INVALID_OPERATION} */ public int setPlaybackRate(int sampleRateInHz) { + if (mState != STATE_INITIALIZED) { + return ERROR_INVALID_OPERATION; + } native_set_playback_rate(sampleRateInHz); return SUCCESS; } - - + + /** - * + * * @param markerInFrames marker in frames * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE}, - * {@link #ERROR_INVALID_OPERATION} + * {@link #ERROR_INVALID_OPERATION} */ public int setNotificationMarkerPosition(int markerInFrames) { + if (mState != STATE_INITIALIZED) { + return ERROR_INVALID_OPERATION; + } return native_set_marker_pos(markerInFrames); } - - + + /** * @param periodInFrames update period in frames * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_INVALID_OPERATION} */ public int setPositionNotificationPeriod(int periodInFrames) { + if (mState != STATE_INITIALIZED) { + return ERROR_INVALID_OPERATION; + } return native_set_pos_update_period(periodInFrames); } - - + + /** * Sets the playback head position. The track must be stopped for the position to be changed. * @param positionInFrames playback head position in frames - * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE} - * @throws java.lang.IllegalStateException if the track is not in - * the {@link #PLAYSTATE_STOPPED} state. + * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE}, + * {@link #ERROR_INVALID_OPERATION} */ - public int setPlaybackHeadPosition(int positionInFrames) - throws IllegalStateException { + public int setPlaybackHeadPosition(int positionInFrames) { synchronized(mPlayStateLock) { if(mPlayState == PLAYSTATE_STOPPED) { return native_set_position(positionInFrames); + } else { + return ERROR_INVALID_OPERATION; } } - throw(new IllegalStateException("setPlaybackHeadPosition() called on a track that is "+ - "not in the PLAYSTATE_STOPPED play state.")); } - + /** * Sets the loop points and the loop count. The loop can be infinite. * @param startInFrames loop start marker in frames * @param endInFrames loop end marker in frames - * @param loopCount the number of times the loop is looped. + * @param loopCount the number of times the loop is looped. * A value of -1 means infinite looping. - * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE} + * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE}, + * {@link #ERROR_INVALID_OPERATION} */ public int setLoopPoints(int startInFrames, int endInFrames, int loopCount) { return native_set_loop(startInFrames, endInFrames, loopCount); } + /** + * Sets the initialization state of the instance. To be used in an AudioTrack subclass + * constructor to set a subclass-specific post-initialization state. + * @param state the state of the AudioTrack instance + */ + protected void setState(int state) { + mState = state; + } + //--------------------------------------------------------- // Transport control methods //-------------------- /** - * Starts playing an AudioTrack. + * Starts playing an AudioTrack. * @throws IllegalStateException */ public void play() @@ -644,7 +725,7 @@ public class AudioTrack if (mState != STATE_INITIALIZED) { throw(new IllegalStateException("play() called on uninitialized AudioTrack.")); } - + synchronized(mPlayStateLock) { native_start(); mPlayState = PLAYSTATE_PLAYING; @@ -671,7 +752,7 @@ public class AudioTrack /** * Pauses the playback of the audio data. * @throws IllegalStateException - */ + */ public void pause() throws IllegalStateException { if (mState != STATE_INITIALIZED) { @@ -685,25 +766,21 @@ public class AudioTrack mPlayState = PLAYSTATE_PAUSED; } } - - + + //--------------------------------------------------------- // Audio data supply //-------------------- /** * Flushes the audio data currently queued for playback. - * @throws IllegalStateException - */ - public void flush() - throws IllegalStateException { - if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("flush() called on uninitialized AudioTrack.")); - } - //logd("flush()"); + */ - // flush the data in native layer - native_flush(); + public void flush() { + if (mState == STATE_INITIALIZED) { + // flush the data in native layer + native_flush(); + } } @@ -712,12 +789,12 @@ public class AudioTrack * @param audioData the array that holds the data to play. * @param offsetInBytes the offset in audioData where the data to play starts. * @param sizeInBytes the number of bytes to read in audioData after the offset. - * @return the number of bytes that were written. - * @throws IllegalStateException - */ - public int write(byte[] audioData,int offsetInBytes, int sizeInBytes) - throws IllegalStateException { - if ((mDataLoadMode == MODE_STATIC) + * @return the number of bytes that were written or -1 if the object wasn't properly + * initialized. + */ + + public int write(byte[] audioData,int offsetInBytes, int sizeInBytes) { + if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) && (sizeInBytes > 0)) { mState = STATE_INITIALIZED; @@ -726,24 +803,24 @@ public class AudioTrack // or: how to update data for static tracks? if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("write() called on uninitialized AudioTrack.")); + return -1; } return native_write_byte(audioData, offsetInBytes, sizeInBytes); } - - + + /** * Writes the audio data to the audio hardware for playback. * @param audioData the array that holds the data to play. * @param offsetInShorts the offset in audioData where the data to play starts. * @param sizeInShorts the number of bytes to read in audioData after the offset. - * @return the number of shorts that were written. - * @throws IllegalStateException - */ - public int write(short[] audioData, int offsetInShorts, int sizeInShorts) - throws IllegalStateException { - if ((mDataLoadMode == MODE_STATIC) + * @return the number of shorts that were written or -1 if the object wasn't properly + * initialized. + */ + + public int write(short[] audioData, int offsetInShorts, int sizeInShorts) { + if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) && (sizeInShorts > 0)) { mState = STATE_INITIALIZED; @@ -752,13 +829,13 @@ public class AudioTrack // or: how to update data for static tracks? if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("write() called on uninitialized AudioTrack.")); + return -1; } return native_write_short(audioData, offsetInShorts, sizeInShorts); } - - + + /** * Notifies the native resource to reuse the audio data already loaded in the native * layer. This call is only valid with AudioTrack instances that don't use the streaming @@ -791,7 +868,7 @@ public class AudioTrack /** - * Interface definition for a callback to be invoked for each periodic AudioTrack + * Interface definition for a callback to be invoked for each periodic AudioTrack * update during playback. The update interval is set by setPositionNotificationPeriod(). */ public interface OnPeriodicNotificationListener { @@ -858,28 +935,28 @@ public class AudioTrack if (track == null) { return; } - + if (track.mNativeEventHandler != null) { Message m = track.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); track.mNativeEventHandler.sendMessage(m); } } - - + + //--------------------------------------------------------- // Native methods called from the Java side //-------------------- - private native final int native_setup(Object audiotrack_this, - int streamType, int sampleRate, int nbChannels, int audioFormat, + private native final int native_setup(Object audiotrack_this, + int streamType, int sampleRate, int nbChannels, int audioFormat, int buffSizeInBytes, int mode); private native final void native_finalize(); - + private native final void native_release(); - private native final void native_start(); + private native final void native_start(); private native final void native_stop(); @@ -887,34 +964,36 @@ public class AudioTrack private native final void native_flush(); - private native final int native_write_byte(byte[] audioData, + private native final int native_write_byte(byte[] audioData, int offsetInBytes, int sizeInBytes); - - private native final int native_write_short(short[] audioData, + + private native final int native_write_short(short[] audioData, int offsetInShorts, int sizeInShorts); - + private native final int native_reload_static(); private native final int native_get_native_frame_count(); private native final void native_setVolume(float leftVolume, float rightVolume); - + private native final void native_set_playback_rate(int sampleRateInHz); private native final int native_get_playback_rate(); - + private native final int native_set_marker_pos(int marker); private native final int native_get_marker_pos(); - + private native final int native_set_pos_update_period(int updatePeriod); private native final int native_get_pos_update_period(); - + private native final int native_set_position(int position); private native final int native_get_position(); - + private native final int native_set_loop(int start, int end, int loopCount); - + static private native final int native_get_output_sample_rate(); - + static private native final int native_get_min_buff_size( + int sampleRateInHz, int channelConfig, int audioFormat); + //--------------------------------------------------------- // Utility methods @@ -928,7 +1007,4 @@ public class AudioTrack Log.e(TAG, "[ android.media.AudioTrack ] " + msg); } -} - - - +}
\ No newline at end of file diff --git a/media/java/android/media/JetPlayer.java b/media/java/android/media/JetPlayer.java index b9268d5..bfa2f80 100644 --- a/media/java/android/media/JetPlayer.java +++ b/media/java/android/media/JetPlayer.java @@ -17,9 +17,11 @@ package android.media; +import java.io.FileDescriptor; import java.lang.ref.WeakReference; import java.lang.CloneNotSupportedException; +import android.content.res.AssetFileDescriptor; import android.os.Looper; import android.os.Handler; import android.os.Message; @@ -29,9 +31,7 @@ import android.util.Log; * JetPlayer provides access to JET content playback and control. * <p> * Use <code>JetPlayer.getJetPlayer()</code> to get an instance of this class. - * There can only be one instance of this class at any one time. * - * @hide */ public class JetPlayer { @@ -39,15 +39,29 @@ public class JetPlayer // Constants //------------------------ /** - * The maximum number of simultaneous tracks. Use {@link #getMaxTracks()} to + * The maximum number of simultaneous tracks. Use __link #getMaxTracks()} to * access this value. */ - protected static int MAXTRACKS = 32; + private static int MAXTRACKS = 32; - // These constants are to be kept in sync with the ones in include/media/JetPlayer.h - protected static final int JET_USERID_UPDATE = 1; - protected static final int JET_NUMQUEUEDSEGMENT_UPDATE = 2; - protected static final int JET_PAUSE_UPDATE = 3; + // to keep in sync with the JetPlayer class constants + // defined in frameworks/base/include/media/JetPlayer.h + private static final int JET_EVENT = 1; + private static final int JET_USERID_UPDATE = 2; + private static final int JET_NUMQUEUEDSEGMENT_UPDATE = 3; + private static final int JET_PAUSE_UPDATE = 4; + + // to keep in sync with external/sonivox/arm-wt-22k/lib_src/jet_data.h + // Encoding of event information on 32 bits + private static final int JET_EVENT_VAL_MASK = 0x0000007f; // mask for value + private static final int JET_EVENT_CTRL_MASK = 0x00003f80; // mask for controller + private static final int JET_EVENT_CHAN_MASK = 0x0003c000; // mask for channel + private static final int JET_EVENT_TRACK_MASK = 0x00fc0000; // mask for track number + private static final int JET_EVENT_SEG_MASK = 0xff000000; // mask for segment ID + private static final int JET_EVENT_CTRL_SHIFT = 7; // shift to get controller number to bit 0 + private static final int JET_EVENT_CHAN_SHIFT = 14; // shift to get MIDI channel to bit 0 + private static final int JET_EVENT_TRACK_SHIFT = 18; // shift to get track ID to bit 0 + private static final int JET_EVENT_SEG_SHIFT = 24; // shift to get segment ID to bit 0 //-------------------------------------------- @@ -56,13 +70,20 @@ public class JetPlayer private EventHandler mNativeEventHandler = null; /** - * Lock to protect event listener updates against event notifications + * Lock to protect status listener updates against status change notifications */ - protected final Object mStatusListenerLock = new Object(); + private final Object mStatusListenerLock = new Object(); - protected JetStatusUpdateListener mJetStatusUpdateListener = null; + /** + * Lock to protect the event listener updates against event notifications + */ + private final Object mEventListenerLock = new Object(); + + private JetStatusUpdateListener mJetStatusUpdateListener = null; - protected static JetPlayer singletonRef; + private JetEventListener mJetEventListener = null; + + private static JetPlayer singletonRef; //-------------------------------- @@ -136,8 +157,14 @@ public class JetPlayer //-------------------------------------------- // Jet functionality //------------------------ - public boolean openJetFile(String path) { - return native_openJetFile(path); + public boolean loadJetFile(String path) { + return native_loadJetFromFile(path); + } + + + public boolean loadJetFile(AssetFileDescriptor afd) { + return native_loadJetFromFileD( + afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); } @@ -195,6 +222,10 @@ public class JetPlayer } + public boolean clearQueue() { + return native_clearQueue(); + } + //--------------------------------------------------------- // Internal class to handle events posted from native code @@ -211,28 +242,42 @@ public class JetPlayer @Override public void handleMessage(Message msg) { switch(msg.what) { + case JET_EVENT: + synchronized (mEventListenerLock) { + if (mJetEventListener != null) { + // call the appropriate listener after decoding the event parameters + // encoded in msg.arg1 + mJetEventListener.onJetEvent( + mJet, + (short)((msg.arg1 & JET_EVENT_SEG_MASK) >> JET_EVENT_SEG_SHIFT), + (byte) ((msg.arg1 & JET_EVENT_TRACK_MASK) >> JET_EVENT_TRACK_SHIFT), + (byte) ((msg.arg1 & JET_EVENT_CHAN_MASK) >> JET_EVENT_CHAN_SHIFT), + (byte) ((msg.arg1 & JET_EVENT_CTRL_MASK) >> JET_EVENT_CTRL_SHIFT), + (byte) (msg.arg1 & JET_EVENT_VAL_MASK) ); + } + } + return; case JET_USERID_UPDATE: synchronized (mStatusListenerLock) { if (mJetStatusUpdateListener != null) { - mJetStatusUpdateListener.onJetUserIdUpdate(msg.arg1, msg.arg2); + mJetStatusUpdateListener.onJetUserIdUpdate(mJet, msg.arg1, msg.arg2); } } return; case JET_NUMQUEUEDSEGMENT_UPDATE: synchronized (mStatusListenerLock) { if (mJetStatusUpdateListener != null) { - mJetStatusUpdateListener.onJetNumQueuedSegmentUpdate(msg.arg1); + mJetStatusUpdateListener.onJetNumQueuedSegmentUpdate(mJet, msg.arg1); } } return; case JET_PAUSE_UPDATE: synchronized (mStatusListenerLock) { if (mJetStatusUpdateListener != null) - mJetStatusUpdateListener.onJetPauseUpdate(msg.arg1); + mJetStatusUpdateListener.onJetPauseUpdate(mJet, msg.arg1); } return; - default: loge("Unknown message type " + msg.what); return; @@ -242,7 +287,7 @@ public class JetPlayer //-------------------------------------------- - // Jet event listener + // Jet status update listener //------------------------ public void setStatusUpdateListener(JetStatusUpdateListener listener) { synchronized(mStatusListenerLock) { @@ -255,31 +300,66 @@ public class JetPlayer } /** - * Handles the notification when the JET segment userID is updated. + * Handles the notification when the JET status is updated. */ public interface JetStatusUpdateListener { /** * Callback for when JET's currently playing segment userID is updated. * + * @param player the JET player the status update is coming from * @param userId the ID of the currently playing segment * @param repeatCount the repetition count for the segment (0 means it plays once) */ - void onJetUserIdUpdate(int userId, int repeatCount); + void onJetUserIdUpdate(JetPlayer player, int userId, int repeatCount); /** * Callback for when JET's number of queued segments is updated. * + * @param player the JET player the status update is coming from * @param nbSegments the number of segments in the JET queue */ - void onJetNumQueuedSegmentUpdate(int nbSegments); + void onJetNumQueuedSegmentUpdate(JetPlayer player, int nbSegments); /** * Callback for when JET pause state is updated. * + * @param player the JET player the status update is coming from * @param paused indicates whether JET is paused or not */ - void onJetPauseUpdate(int paused); - }; + void onJetPauseUpdate(JetPlayer player, int paused); + } + + + //-------------------------------------------- + // Jet event listener + //------------------------ + public void setEventListener(JetEventListener listener) { + synchronized(mEventListenerLock) { + mJetEventListener = listener; + } + + if ((listener != null) && (mNativeEventHandler == null)) { + createNativeEventHandler(); + } + } + + /** + * Handles the notification when the JET engine generates an event. + */ + public interface JetEventListener { + /** + * Callback for when the JET engine generates a new event. + * + * @param player the JET player the event is coming from + * @param segment 8 bit unsigned value + * @param track 6 bit unsigned value + * @param channel 4 bit unsigned value + * @param controller 7 bit unsigned value + * @param value 7 bit unsigned value + */ + void onJetEvent(JetPlayer player, + short segment, byte track, byte channel, byte controller, byte value); + } //-------------------------------------------- @@ -289,7 +369,8 @@ public class JetPlayer int maxTracks, int trackBufferSize); private native final void native_finalize(); private native final void native_release(); - private native final boolean native_openJetFile(String pathToJetFile); + private native final boolean native_loadJetFromFile(String pathToJetFile); + private native final boolean native_loadJetFromFileD(FileDescriptor fd, long offset, long len); private native final boolean native_closeJetFile(); private native final boolean native_playJet(); private native final boolean native_pauseJet(); @@ -300,7 +381,8 @@ public class JetPlayer private native final boolean native_setMuteFlags(int muteFlags, boolean sync); private native final boolean native_setMuteArray(boolean[]muteArray, boolean sync); private native final boolean native_setMuteFlag(int trackId, boolean muteFlag, boolean sync); - private native final boolean native_triggerClip(int clipId); + private native final boolean native_triggerClip(int clipId); + private native final boolean native_clearQueue(); //--------------------------------------------------------- // Called exclusively by native code diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 22361aa..f05842d 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -102,13 +102,17 @@ public class MediaFile { addFileType("AMR", FILE_TYPE_AMR, "audio/amr"); addFileType("AWB", FILE_TYPE_AWB, "audio/amr-wb"); addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma"); - addFileType("OGG", FILE_TYPE_OGG, "application/ogg"); + addFileType("OGG", FILE_TYPE_OGG, "application/ogg"); + addFileType("OGA", FILE_TYPE_OGG, "application/ogg"); addFileType("MID", FILE_TYPE_MID, "audio/midi"); + addFileType("MIDI", FILE_TYPE_MID, "audio/midi"); addFileType("XMF", FILE_TYPE_MID, "audio/midi"); addFileType("RTTTL", FILE_TYPE_MID, "audio/midi"); addFileType("SMF", FILE_TYPE_SMF, "audio/sp-midi"); addFileType("IMY", FILE_TYPE_IMY, "audio/imelody"); + addFileType("RTX", FILE_TYPE_MID, "audio/midi"); + addFileType("OTA", FILE_TYPE_MID, "audio/midi"); addFileType("MP4", FILE_TYPE_MP4, "video/mp4"); addFileType("M4V", FILE_TYPE_M4V, "video/mp4"); diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 1a82654..601557d 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -39,11 +39,11 @@ import java.lang.ref.WeakReference; /** * MediaPlayer class can be used to control playback * of audio/video files and streams. An example on how to use the methods in - * this class can be found in <a href="../widget/VideoView.html">VideoView</a>. - * Please see <a href="../../../toolbox/apis/media.html">Android Media APIs</a> + * this class can be found in {@link android.widget.VideoView}. + * Please see <a href="{@docRoot}guide/topics/media/index.html">Audio and Video</a> * for additional help using MediaPlayer. * - * <p>Topics covered are: + * <p>Topics covered here are: * <ol> * <li><a href="#StateDiagram">State Diagram</a> * <li><a href="#Valid_and_Invalid_States">Valid and Invalid States</a> diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 651cc41..4a30114 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -24,7 +24,7 @@ import java.io.IOException; * Used to record audio and video. The recording control is based on a * simple state machine (see below). * - * <p><img src="../../../images/mediarecorder_state_diagram.gif" border="0" /> + * <p><img src="{@docRoot}images/mediarecorder_state_diagram.gif" border="0" /> * </p> * * <p>A common case of using MediaRecorder to record audio works as follows: @@ -42,8 +42,8 @@ import java.io.IOException; * recorder.release(); // Now the object cannot be reused * </pre> * - * <p>See the <a href="../../../toolbox/apis/media.html">Android Media APIs</a> - * page for additional help with using MediaRecorder. + * <p>See the <a href="{@docRoot}guide/topics/media/index.html">Audio and Video</a> + * documentation for additional help with using MediaRecorder. */ public class MediaRecorder { diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 38203b6..fc8476d 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -118,6 +118,7 @@ public class MediaScanner private static final String NOTIFICATIONS_DIR = "/notifications/"; private static final String ALARMS_DIR = "/alarms/"; private static final String MUSIC_DIR = "/music/"; + private static final String PODCAST_DIR = "/podcasts/"; private static final String[] ID3_GENRES = { // ID3v1 Genres @@ -455,8 +456,9 @@ public class MediaScanner boolean ringtones = (path.indexOf(RINGTONES_DIR) > 0); boolean notifications = (path.indexOf(NOTIFICATIONS_DIR) > 0); boolean alarms = (path.indexOf(ALARMS_DIR) > 0); + boolean podcasts = (path.indexOf(PODCAST_DIR) > 0); boolean music = (path.indexOf(MUSIC_DIR) > 0) || - (!ringtones && !notifications && !alarms); + (!ringtones && !notifications && !alarms && !podcasts); if (mFileType == MediaFile.FILE_TYPE_MP3 || mFileType == MediaFile.FILE_TYPE_MP4 || @@ -473,7 +475,7 @@ public class MediaScanner // we used to compute the width and height but it's not worth it } - result = endFile(entry, ringtones, notifications, alarms, music); + result = endFile(entry, ringtones, notifications, alarms, music, podcasts); } } catch (RemoteException e) { Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e); @@ -586,7 +588,8 @@ public class MediaScanner return map; } - public Uri endFile(FileCacheEntry entry, boolean ringtones, boolean notifications, boolean alarms, boolean music) + private Uri endFile(FileCacheEntry entry, boolean ringtones, boolean notifications, + boolean alarms, boolean music, boolean podcasts) throws RemoteException { // update database Uri tableUri; @@ -634,6 +637,7 @@ public class MediaScanner values.put(Audio.Media.IS_NOTIFICATION, notifications); values.put(Audio.Media.IS_ALARM, alarms); values.put(Audio.Media.IS_MUSIC, music); + values.put(Audio.Media.IS_PODCAST, podcasts); } else if (isImage) { // nothing right now } |
