public final class VolumeShaper {
/* member variables */
private int mId;
- private final WeakReference<PlayerBase> mPlayerBase;
- private final WeakReference<PlayerProxy> mPlayerProxy;
+ private final WeakReference<PlayerBase> mWeakPlayerBase;
+ private final WeakReference<PlayerProxy> mWeakPlayerProxy;
+ private PlayerProxy mPlayerProxy;
/**
* Constructs a {@code VolumeShaper} from a {@link VolumeShaper.Configuration} and an
/* package */ VolumeShaper(
@NonNull Configuration configuration, @NonNull PlayerBase playerBase) {
- mPlayerBase = new WeakReference<PlayerBase>(playerBase);
+ mWeakPlayerBase = new WeakReference<PlayerBase>(playerBase);
mPlayerProxy = null;
+ mWeakPlayerProxy = null;
mId = applyPlayer(configuration, new Operation.Builder().defer().build());
}
/**
* @hide
- * TODO SystemApi
* Constructs a {@code VolumeShaper} from a {@link VolumeShaper.Configuration} and a
* {@code PlayerProxy} object. The PlayerProxy object requires that the configuration
* be set with a system VolumeShaper id (this is a reserved value).
* @param playerProxy
*/
public VolumeShaper(
- @NonNull Configuration configuration, @NonNull PlayerProxy playerProxy) {
+ @NonNull Configuration configuration,
+ @NonNull PlayerProxy playerProxy,
+ boolean keepReference) {
if (configuration.getId() < 0) {
throw new IllegalArgumentException("playerProxy configuration id must be specified");
}
- mPlayerProxy = new WeakReference<PlayerProxy>(playerProxy);
- mPlayerBase = null;
+ if (keepReference) {
+ mPlayerProxy = playerProxy;
+ mWeakPlayerProxy = null;
+ } else {
+ mWeakPlayerProxy = new WeakReference<PlayerProxy>(playerProxy);
+ mPlayerProxy = null;
+ }
+ mWeakPlayerBase = null;
mId = applyPlayer(configuration, new Operation.Builder().defer().build());
}
} catch (IllegalStateException ise) {
; // ok
}
- if (mPlayerBase != null) {
- mPlayerBase.clear();
+ if (mWeakPlayerBase != null) {
+ mWeakPlayerBase.clear();
}
- if (mPlayerProxy != null) {
- mPlayerProxy.clear();
+ if (mWeakPlayerProxy != null) {
+ mWeakPlayerProxy.clear();
}
+ mPlayerProxy = null;
}
@Override
@NonNull VolumeShaper.Configuration configuration,
@NonNull VolumeShaper.Operation operation) {
final int id;
- if (mPlayerProxy != null) {
+ if (mPlayerProxy != null || mWeakPlayerProxy != null) {
// The PlayerProxy accepts only one way transactions so
// the Configuration must have an id set to one of the system
// ids (a positive value less than 16).
- PlayerProxy player = mPlayerProxy.get();
+ PlayerProxy player = mWeakPlayerProxy != null ? mWeakPlayerProxy.get() : mPlayerProxy;
if (player == null) {
throw new IllegalStateException("player deallocated");
}
throw new IllegalArgumentException("proxy requires configuration with id");
}
player.applyVolumeShaper(configuration, operation);
- } else if (mPlayerBase != null) {
- PlayerBase player = mPlayerBase.get();
+ } else if (mWeakPlayerBase != null) {
+ PlayerBase player = mWeakPlayerBase.get();
if (player == null) {
throw new IllegalStateException("player deallocated");
}
*/
private @NonNull VolumeShaper.State getStatePlayer(int id) {
final VolumeShaper.State state;
- if (mPlayerProxy != null) {
- PlayerProxy player = mPlayerProxy.get();
- if (player == null) {
- throw new IllegalStateException("player deallocated");
- }
+ if (mPlayerProxy != null || mWeakPlayerProxy != null) {
throw new IllegalStateException("getStatePlayer not permitted through proxy");
- } else if (mPlayerBase != null) {
- PlayerBase player = mPlayerBase.get();
+ } else if (mWeakPlayerBase != null) {
+ PlayerBase player = mWeakPlayerBase.get();
if (player == null) {
throw new IllegalStateException("player deallocated");
}
import android.media.IPlaybackConfigDispatcher;
import android.media.MediaRecorder;
import android.media.PlayerBase;
+import android.media.VolumeShaper;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
public final static String TAG = "AudioService.PlaybackActivityMonitor";
private final static boolean DEBUG = false;
+ private final static int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1;
private ArrayList<PlayMonitorClient> mClients = new ArrayList<PlayMonitorClient>();
// a public client is one that needs an anonymized version of the playback configurations, we
final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
if (checkConfigurationCaller(piid, apc, binderUid)) {
mPlayers.remove(new Integer(piid));
+ final VolumeShaper vs = mDuckVolumeShapers.get(new Integer(piid));
+ if (vs != null) {
+ vs.release();
+ mDuckVolumeShapers.remove(new Integer(piid));
+ }
} else {
Log.e(TAG, "Error releasing player " + piid);
}
private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>();
+ private final VolumeShaper.Configuration DUCK_VSHAPE =
+ new VolumeShaper.Configuration.Builder()
+ .setId(VOLUME_SHAPER_SYSTEM_DUCK_ID)
+ .setCurve(new float[] { 0.f, 1.f } /* times */,
+ new float[] { 1.f, 0.2f } /* volumes */)
+ .setDurationMs(MediaFocusControl.getFocusRampTimeMs(
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION)
+ .build()))
+ .build();
+
+ private final HashMap<Integer, VolumeShaper> mDuckVolumeShapers =
+ new HashMap<Integer, VolumeShaper>();
+
@Override
public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
if (DEBUG) {
// the player is speaking, ducking will make the speech unintelligible
// so let the app handle it instead
return false;
+ } else if (apc.getPlayerType()
+ == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+ // TODO support ducking of SoundPool players
+ return false;
} else {
try {
if (DEBUG) { Log.v(TAG, "ducking player " + piid); }
- //FIXME just a test before we have VolumeShape
- apc.getPlayerProxy().setPan(-1.0f);
+ final VolumeShaper ducker;
+ if (mDuckVolumeShapers.containsKey(new Integer(piid))) {
+ ducker = mDuckVolumeShapers.get(new Integer(piid));
+ } else {
+ ducker = new VolumeShaper(
+ DUCK_VSHAPE,
+ apc.getPlayerProxy(),
+ true /* keepReference */);
+ mDuckVolumeShapers.put(new Integer(piid), ducker);
+ }
+ ducker.apply(VolumeShaper.Operation.PLAY); // duck
mDuckedPlayers.add(piid);
} catch (Exception e) {
Log.e(TAG, "Error ducking player " + piid, e);
try {
if (DEBUG) { Log.v(TAG, "unducking player" + piid); }
mDuckedPlayers.remove(new Integer(piid));
- //FIXME just a test before we have VolumeShape
- apc.getPlayerProxy().setPan(0.0f);
+ if (mDuckVolumeShapers.containsKey(new Integer(piid))) {
+ final VolumeShaper ducker = mDuckVolumeShapers.get(new Integer(piid));
+ ducker.apply(VolumeShaper.Operation.REVERSE); // unduck
+ }
} catch (Exception e) {
Log.e(TAG, "Error unducking player " + piid, e);
}