Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 263b4c97 authored by Andy Hung's avatar Andy Hung
Browse files

Add PlaybackSettings for use with AudioTrack

Change-Id: Ie59686d46869558d489a7600170ddace00e548d5
parent 73d74680
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -14987,6 +14987,7 @@ package android.media {
    method public int getPlayState();
    method public int getPlaybackHeadPosition();
    method public int getPlaybackRate();
    method public android.media.PlaybackSettings getPlaybackSettings();
    method public int getPositionNotificationPeriod();
    method public android.media.AudioDeviceInfo getPreferredOutputDevice();
    method public int getSampleRate();
@@ -15004,6 +15005,7 @@ package android.media {
    method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener);
    method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener, android.os.Handler);
    method public int setPlaybackRate(int);
    method public void setPlaybackSettings(android.media.PlaybackSettings);
    method public int setPositionNotificationPeriod(int);
    method public boolean setPreferredOutputDevice(android.media.AudioDeviceInfo);
    method protected deprecated void setState(int);
@@ -16367,6 +16369,24 @@ package android.media {
    method public abstract void onAudioDeviceConnection();
  }
  public final class PlaybackSettings {
    ctor public PlaybackSettings();
    method public android.media.PlaybackSettings allowDefaults();
    method public int getAudioFallbackMode();
    method public int getAudioStretchMode();
    method public float getPitch();
    method public float getSpeed();
    method public android.media.PlaybackSettings setAudioFallbackMode(int);
    method public android.media.PlaybackSettings setAudioStretchMode(int);
    method public android.media.PlaybackSettings setPitch(float);
    method public android.media.PlaybackSettings setSpeed(float);
    field public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0; // 0x0
    field public static final int AUDIO_FALLBACK_MODE_FAIL = 2; // 0x2
    field public static final int AUDIO_FALLBACK_MODE_MUTE = 1; // 0x1
    field public static final int AUDIO_STRETCH_MODE_DEFAULT = 0; // 0x0
    field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
  }
  public final class Rating implements android.os.Parcelable {
    method public int describeContents();
    method public float getPercentRating();
+20 −0
Original line number Diff line number Diff line
@@ -16199,6 +16199,7 @@ package android.media {
    method public int getPlayState();
    method public int getPlaybackHeadPosition();
    method public int getPlaybackRate();
    method public android.media.PlaybackSettings getPlaybackSettings();
    method public int getPositionNotificationPeriod();
    method public android.media.AudioDeviceInfo getPreferredOutputDevice();
    method public int getSampleRate();
@@ -16216,6 +16217,7 @@ package android.media {
    method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener);
    method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener, android.os.Handler);
    method public int setPlaybackRate(int);
    method public void setPlaybackSettings(android.media.PlaybackSettings);
    method public int setPositionNotificationPeriod(int);
    method public boolean setPreferredOutputDevice(android.media.AudioDeviceInfo);
    method protected deprecated void setState(int);
@@ -17582,6 +17584,24 @@ package android.media {
    method public abstract void onAudioDeviceConnection();
  }
  public final class PlaybackSettings {
    ctor public PlaybackSettings();
    method public android.media.PlaybackSettings allowDefaults();
    method public int getAudioFallbackMode();
    method public int getAudioStretchMode();
    method public float getPitch();
    method public float getSpeed();
    method public android.media.PlaybackSettings setAudioFallbackMode(int);
    method public android.media.PlaybackSettings setAudioStretchMode(int);
    method public android.media.PlaybackSettings setPitch(float);
    method public android.media.PlaybackSettings setSpeed(float);
    field public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0; // 0x0
    field public static final int AUDIO_FALLBACK_MODE_FAIL = 2; // 0x2
    field public static final int AUDIO_FALLBACK_MODE_MUTE = 1; // 0x1
    field public static final int AUDIO_STRETCH_MODE_DEFAULT = 0; // 0x0
    field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
  }
  public final class Rating implements android.os.Parcelable {
    method public int describeContents();
    method public float getPercentRating();
+61 −0
Original line number Diff line number Diff line
@@ -676,6 +676,63 @@ static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env, jobject thi
}


// ----------------------------------------------------------------------------
static void android_media_AudioTrack_set_playback_settings(JNIEnv *env,  jobject thiz,
        jfloatArray floatArray, jintArray intArray) {
    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    if (lpTrack == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException",
            "AudioTrack not initialized");
        return;
    }

    // NOTE: Get<Primitive>ArrayRegion throws ArrayIndexOutOfBoundsException if not valid.
    // TODO: consider the actual occupancy.
    float farray[2];
    int iarray[2];
    if ((env->GetFloatArrayRegion(floatArray, 0, 2, farray), env->ExceptionCheck()) == JNI_FALSE
            &&
        (env->GetIntArrayRegion(intArray, 0, 2, iarray), env->ExceptionCheck()) == JNI_FALSE) {
        // arrays retrieved OK
        AudioPlaybackRate playbackRate;
        playbackRate.mSpeed = farray[0];
        playbackRate.mPitch = farray[1];
        playbackRate.mFallbackMode = (AudioTimestretchFallbackMode)iarray[0];
        playbackRate.mStretchMode = (AudioTimestretchStretchMode)iarray[1];
        if (lpTrack->setPlaybackRate(playbackRate) != OK) {
            jniThrowException(env, "java/lang/IllegalArgumentException",
                    "arguments out of range");
        }
    }
}


// ----------------------------------------------------------------------------
static void android_media_AudioTrack_get_playback_settings(JNIEnv *env,  jobject thiz,
        jfloatArray floatArray, jintArray intArray) {
    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    if (lpTrack == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException",
            "AudioTrack not initialized");
        return;
    }

    AudioPlaybackRate playbackRate = lpTrack->getPlaybackRate();

    float farray[2] = {
            playbackRate.mSpeed,
            playbackRate.mPitch,
    };
    int iarray[2] = {
            playbackRate.mFallbackMode,
            playbackRate.mStretchMode,
    };
    // NOTE: Set<Primitive>ArrayRegion throws ArrayIndexOutOfBoundsException if not valid.
    env->SetFloatArrayRegion(floatArray, 0, 2, farray);
    env->SetIntArrayRegion(intArray, 0, 2, iarray);
}


// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env,  jobject thiz,
        jint markerPos) {
@@ -942,6 +999,10 @@ static JNINativeMethod gMethods[] = {
                             "(I)I",     (void *)android_media_AudioTrack_set_playback_rate},
    {"native_get_playback_rate",
                             "()I",      (void *)android_media_AudioTrack_get_playback_rate},
    {"native_set_playback_settings",
                             "([F[I)V",  (void *)android_media_AudioTrack_set_playback_settings},
    {"native_get_playback_settings",
                             "([F[I)V",  (void *)android_media_AudioTrack_get_playback_settings},
    {"native_set_marker_pos","(I)I",     (void *)android_media_AudioTrack_set_marker_pos},
    {"native_get_marker_pos","()I",      (void *)android_media_AudioTrack_get_marker_pos},
    {"native_set_pos_update_period",
+64 −1
Original line number Diff line number Diff line
@@ -946,12 +946,29 @@ public class AudioTrack
    }

    /**
     * Returns the current playback rate in Hz.
     * Returns the current playback sample rate rate in Hz.
     */
    public int getPlaybackRate() {
        return native_get_playback_rate();
    }

    /**
     * Returns the current playback settings.
     * See {@link #setPlaybackSettings(PlaybackSettings)} to set playback settings
     * @return current {@link PlaybackSettings}.
     * @throws IllegalStateException if track is not initialized.
     */
    public @NonNull PlaybackSettings getPlaybackSettings() {
        float[] floatArray = new float[2];
        int[] intArray = new int[2];
        native_get_playback_settings(floatArray, intArray);
        return new PlaybackSettings()
                .setSpeed(floatArray[0])
                .setPitch(floatArray[1])
                .setAudioFallbackMode(intArray[0])
                .setAudioStretchMode(intArray[1]);
    }

    /**
     * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT}
     * and {@link AudioFormat#ENCODING_PCM_8BIT}.
@@ -1307,6 +1324,7 @@ public class AudioTrack
     * playback to last twice as long, but will also result in a pitch shift down by one octave.
     * The valid sample rate range is from 1 Hz to twice the value returned by
     * {@link #getNativeOutputSampleRate(int)}.
     * Use {@link #setPlaybackSettings(PlaybackSettings)} for speed control.
     * @param sampleRateInHz the sample rate expressed in Hz
     * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
     *    {@link #ERROR_INVALID_OPERATION}
@@ -1322,6 +1340,42 @@ public class AudioTrack
    }


    /**
     * Sets the playback settings.
     * This method returns failure if it cannot apply the playback settings.
     * One possible cause is that the parameters for speed or pitch are out of range.
     * Another possible cause is that the <code>AudioTrack</code> is streaming
     * (see {@link #MODE_STREAM}) and the
     * buffer size is too small. For speeds greater than 1.0f, the <code>AudioTrack</code> buffer
     * on configuration must be larger than the speed multiplied by the minimum size
     * {@link #getMinBufferSize(int, int, int)}) to allow proper playback.
     * @param settings see {@link PlaybackSettings}. In particular,
     * speed, pitch, and audio mode should be set.
     * @throws IllegalArgumentException if the settings are invalid or not accepted.
     * @throws IllegalStateException if track is not initialized.
     */
    public void setPlaybackSettings(@NonNull PlaybackSettings settings) {
        if (settings == null) {
            throw new IllegalArgumentException("settings is null");
        }
        float[] floatArray;
        int[] intArray;
        try {
            floatArray = new float[] {
                    settings.getSpeed(),
                    settings.getPitch(),
            };
            intArray = new int[] {
                    settings.getAudioFallbackMode(),
                    settings.getAudioStretchMode(),
            };
        } catch (IllegalStateException e) {
            throw new IllegalArgumentException(e);
        }
        native_set_playback_settings(floatArray, intArray);
    }


    /**
     * Sets the position of the notification marker.  At most one marker can be active.
     * @param markerInFrames marker position in wrapping frame units similar to
@@ -2207,6 +2261,15 @@ public class AudioTrack
    private native final int native_set_playback_rate(int sampleRateInHz);
    private native final int native_get_playback_rate();

    // floatArray must be a non-null array of length >= 2
    // [0] is speed
    // [1] is pitch
    // intArray must be a non-null array of length >= 2
    // [0] is audio fallback mode
    // [1] is audio stretch mode
    private native final void native_set_playback_settings(float[] floatArray, int[] intArray);
    private native final void native_get_playback_settings(float[] floatArray, int[] intArray);

    private native final int native_set_marker_pos(int marker);
    private native final int native_get_marker_pos();

+206 −0
Original line number Diff line number Diff line
/*
 * Copyright 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import android.annotation.IntDef;

/**
 * Structure for common playback settings.
 *
 * Used by {@link AudioTrack} {@link AudioTrack#getPlaybackSettings()} and
 * {@link AudioTrack#setPlaybackSettings(PlaybackSettings)}
 * to control playback behavior.
 * <p> <strong>audio fallback mode:</strong>
 * select out-of-range parameter handling.
 * <ul>
 * <li> {@link PlaybackSettings#AUDIO_FALLBACK_MODE_DEFAULT}:
 *   System will determine best handling. </li>
 * <li> {@link PlaybackSettings#AUDIO_FALLBACK_MODE_MUTE}:
 *   Play silence for settings normally out of range.</li>
 * <li> {@link PlaybackSettings#AUDIO_FALLBACK_MODE_FAIL}:
 *   Return {@link java.lang.IllegalArgumentException} from
 *   <code>AudioTrack.setPlaybackSettings(PlaybackSettings)</code>.</li>
 * </ul>
 * <p> <strong>audio stretch mode:</strong> select
 * timestretch handling.
 * <ul>
 * <li> {@link PlaybackSettings#AUDIO_STRETCH_MODE_DEFAULT}:
 *   System will determine best selection. </li>
 * <li> {@link PlaybackSettings#AUDIO_STRETCH_MODE_VOICE}:
 *   Content is primarily voice.</li>
 * </ul>
 * <p> <strong>pitch:</strong> increases or decreases the tonal frequency of the audio content.
 * It is expressed as a multiplicative factor, where normal pitch is 1.0f.
 * <p> <strong>speed:</strong> increases or decreases the time to
 * play back a set of audio or video frames.
 * It is expressed as a multiplicative factor, where normal speed is 1.0f.
 * <p> Different combinations of speed and pitch may be used for audio playback;
 * some common ones:
 * <ul>
 * <li> <em>Pitch equals 1.0f.</em> Speed change will be done with pitch preserved,
 * often called <em>timestretching</em>.</li>
 * <li> <em>Pitch equals speed.</em> Speed change will be done by <em>resampling</em>,
 * similar to {@link AudioTrack#setPlaybackRate(int)}.</li>
 * </ul>
 */
public final class PlaybackSettings {
    /** @hide */
    @IntDef(
        value = {
                AUDIO_FALLBACK_MODE_DEFAULT,
                AUDIO_FALLBACK_MODE_MUTE,
                AUDIO_FALLBACK_MODE_FAIL,
        }
    )
    @Retention(RetentionPolicy.SOURCE)
    public @interface AudioFallbackMode {}
    public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0;
    public static final int AUDIO_FALLBACK_MODE_MUTE = 1;
    public static final int AUDIO_FALLBACK_MODE_FAIL = 2;

    /** @hide */
    @IntDef(
        value = {
                AUDIO_STRETCH_MODE_DEFAULT,
                AUDIO_STRETCH_MODE_VOICE,
        }
    )
    @Retention(RetentionPolicy.SOURCE)
    public @interface AudioStretchMode {}
    public static final int AUDIO_STRETCH_MODE_DEFAULT = 0;
    public static final int AUDIO_STRETCH_MODE_VOICE = 1;

    // flags to indicate which settings are actually set
    private static final int SET_SPEED               = 1 << 0;
    private static final int SET_PITCH               = 1 << 1;
    private static final int SET_AUDIO_FALLBACK_MODE = 1 << 2;
    private static final int SET_AUDIO_STRETCH_MODE  = 1 << 3;
    private int mSet = 0;

    // settings
    private int mAudioFallbackMode = AUDIO_FALLBACK_MODE_DEFAULT;
    private int mAudioStretchMode = AUDIO_STRETCH_MODE_DEFAULT;
    private float mPitch = 1.0f;
    private float mSpeed = 1.0f;

    /**
     * Allows defaults to be returned for properties not set.
     * Otherwise a {@link java.lang.IllegalArgumentException} exception
     * is raised when getting those properties
     * which have defaults but have never been set.
     * @return this <code>PlaybackSettings</code> instance.
     */
    public PlaybackSettings allowDefaults() {
        mSet |= SET_AUDIO_FALLBACK_MODE | SET_AUDIO_STRETCH_MODE | SET_PITCH | SET_SPEED;
        return this;
    }

    /**
     * Sets the audio fallback mode.
     * @param audioFallbackMode
     * @return this <code>PlaybackSettings</code> instance.
     */
    public PlaybackSettings setAudioFallbackMode(@AudioFallbackMode int audioFallbackMode) {
        mAudioFallbackMode = audioFallbackMode;
        mSet |= SET_AUDIO_FALLBACK_MODE;
        return this;
    }

    /**
     * Retrieves the audio fallback mode.
     * @return audio fallback mode
     * @throws IllegalStateException if the audio fallback mode is not set.
     */
    public @AudioFallbackMode int getAudioFallbackMode() {
        if ((mSet & SET_AUDIO_FALLBACK_MODE) == 0) {
            throw new IllegalStateException("audio fallback mode not set");
        }
        return mAudioFallbackMode;
    }

    /**
     * Sets the audio stretch mode.
     * @param audioStretchMode
     * @return this <code>PlaybackSettings</code> instance.
     */
    public PlaybackSettings setAudioStretchMode(@AudioStretchMode int audioStretchMode) {
        mAudioStretchMode = audioStretchMode;
        mSet |= SET_AUDIO_STRETCH_MODE;
        return this;
    }

    /**
     * Retrieves the audio stretch mode.
     * @return audio stretch mode
     * @throws IllegalStateException if the audio stretch mode is not set.
     */
    public @AudioStretchMode int getAudioStretchMode() {
        if ((mSet & SET_AUDIO_STRETCH_MODE) == 0) {
            throw new IllegalStateException("audio stretch mode not set");
        }
        return mAudioStretchMode;
    }

    /**
     * Sets the pitch factor.
     * @param pitch
     * @return this <code>PlaybackSettings</code> instance.
     */
    public PlaybackSettings setPitch(float pitch) {
        mPitch = pitch;
        mSet |= SET_PITCH;
        return this;
    }

    /**
     * Retrieves the pitch factor.
     * @return pitch
     * @throws IllegalStateException if pitch is not set.
     */
    public float getPitch() {
        if ((mSet & SET_PITCH) == 0) {
            throw new IllegalStateException("pitch not set");
        }
        return mPitch;
    }

    /**
     * Sets the speed factor.
     * @param speed
     * @return this <code>PlaybackSettings</code> instance.
     */
    public PlaybackSettings setSpeed(float speed) {
        mSpeed = speed;
        mSet |= SET_SPEED;
        return this;
    }

    /**
     * Retrieves the speed factor.
     * @return speed
     * @throws IllegalStateException if speed is not set.
     */
    public float getSpeed() {
        if ((mSet & SET_SPEED) == 0) {
            throw new IllegalStateException("speed not set");
        }
        return mSpeed;
    }
}