Allow sample rate UNSPECIFIED to mean a route-dependent value
Bug: 25641253
Bug: 21019153
Change-Id: I3b8e01b6ea09f42c813ab1a0f3f625dc3803cc0b
diff --git a/api/current.txt b/api/current.txt
index 691ec04a..bb2c332 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -19772,6 +19772,7 @@
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_8BIT = 3; // 0x3
field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
+ field public static final int SAMPLE_RATE_UNSPECIFIED = 0; // 0x0
}
public static class AudioFormat.Builder {
diff --git a/api/system-current.txt b/api/system-current.txt
index 2374f01..defb4c5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -21268,6 +21268,7 @@
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_8BIT = 3; // 0x3
field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
+ field public static final int SAMPLE_RATE_UNSPECIFIED = 0; // 0x0
}
public static class AudioFormat.Builder {
diff --git a/api/test-current.txt b/api/test-current.txt
index a86be54..da6fa6c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -19781,6 +19781,7 @@
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_8BIT = 3; // 0x3
field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
+ field public static final int SAMPLE_RATE_UNSPECIFIED = 0; // 0x0
}
public static class AudioFormat.Builder {
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index d0326f1..6904fda 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -180,9 +180,13 @@
// ----------------------------------------------------------------------------
static jint
android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jobject jaa, jint sampleRateInHertz, jint channelMask, jint channelIndexMask,
+ jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask,
jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName)
{
+ jint elements[1];
+ env->GetIntArrayRegion(jSampleRate, 0, 1, elements);
+ int sampleRateInHertz = elements[0];
+
//ALOGV(">> Entering android_media_AudioRecord_setup");
//ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d",
// sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes);
@@ -304,6 +308,11 @@
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
+ {
+ const jint elements[1] = { (jint) lpRecorder->getSampleRate() };
+ env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
+ }
+
{ // scope for the lock
Mutex::Autolock l(sLock);
sAudioRecordCallBackCookies.add(lpCallbackData);
@@ -717,7 +726,7 @@
// name, signature, funcPtr
{"native_start", "(II)I", (void *)android_media_AudioRecord_start},
{"native_stop", "()V", (void *)android_media_AudioRecord_stop},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;IIIII[ILjava/lang/String;)I",
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;)I",
(void *)android_media_AudioRecord_setup},
{"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
{"native_release", "()V", (void *)android_media_AudioRecord_release},
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 1ab9504..84cc185 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -215,9 +215,13 @@
static jint
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jobject jaa,
- jint sampleRateInHertz, jint channelPositionMask, jint channelIndexMask,
+ jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) {
+ jint elements[1];
+ env->GetIntArrayRegion(jSampleRate, 0, 1, elements);
+ int sampleRateInHertz = elements[0];
+
ALOGV("sampleRate=%d, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d",
sampleRateInHertz, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes);
@@ -370,6 +374,11 @@
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
+ {
+ const jint elements[1] = { (jint) lpTrack->getSampleRate() };
+ env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
+ }
+
{ // scope for the lock
Mutex::Autolock l(sLock);
sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
@@ -1114,7 +1123,7 @@
{"native_stop", "()V", (void *)android_media_AudioTrack_stop},
{"native_pause", "()V", (void *)android_media_AudioTrack_pause},
{"native_flush", "()V", (void *)android_media_AudioTrack_flush},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;IIIIII[I)I",
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[I)I",
(void *)android_media_AudioTrack_setup},
{"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
{"native_release", "()V", (void *)android_media_AudioTrack_release},
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 22f4f04..a0dfaa0f 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -335,6 +335,24 @@
CHANNEL_OUT_LOW_FREQUENCY);
// CHANNEL_OUT_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_OUT_ALL
+ /** Minimum value for sample rate,
+ * assuming AudioTrack and AudioRecord share the same limitations.
+ * @hide
+ */
+ // never unhide
+ public static final int SAMPLE_RATE_HZ_MIN = 4000;
+ /** Maximum value for sample rate,
+ * assuming AudioTrack and AudioRecord share the same limitations.
+ * @hide
+ */
+ // never unhide
+ public static final int SAMPLE_RATE_HZ_MAX = 192000;
+ /** Sample rate will be a route-dependent value.
+ * For AudioTrack, it is usually the sink sample rate,
+ * and for AudioRecord it is usually the source sample rate.
+ */
+ public static final int SAMPLE_RATE_UNSPECIFIED = 0;
+
/**
* @hide
* Return the input channel mask corresponding to an output channel mask.
@@ -561,7 +579,7 @@
}
/**
- * Constructor used by the JNI
+ * Constructor used by the JNI. Parameters are not checked for validity.
*/
// Update sound trigger JNI in core/jni/android_hardware_SoundTrigger.cpp when modifying this
// constructor
@@ -610,12 +628,9 @@
/**
* Return the sample rate.
* @return one of the values that can be set in {@link Builder#setSampleRate(int)} or
- * 0 if not set.
+ * {@link #SAMPLE_RATE_UNSPECIFIED} if not set.
*/
public int getSampleRate() {
- if ((mPropertySetMask & AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) == 0) {
- return 0;
- }
return mSampleRate;
}
@@ -687,7 +702,7 @@
*/
public static class Builder {
private int mEncoding = ENCODING_INVALID;
- private int mSampleRate = 0;
+ private int mSampleRate = SAMPLE_RATE_UNSPECIFIED;
private int mChannelMask = CHANNEL_INVALID;
private int mChannelIndexMask = 0;
private int mPropertySetMask = AUDIO_FORMAT_HAS_PROPERTY_NONE;
@@ -718,6 +733,8 @@
public AudioFormat build() {
AudioFormat af = new AudioFormat(1980/*ignored*/);
af.mEncoding = mEncoding;
+ // not calling setSampleRate is equivalent to calling
+ // setSampleRate(SAMPLE_RATE_UNSPECIFIED)
af.mSampleRate = mSampleRate;
af.mChannelMask = mChannelMask;
af.mChannelIndexMask = mChannelIndexMask;
@@ -795,7 +812,7 @@
* are specified but do not have the same channel count.
*/
public @NonNull Builder setChannelMask(int channelMask) {
- if (channelMask == 0) {
+ if (channelMask == CHANNEL_INVALID) {
throw new IllegalArgumentException("Invalid zero channel mask");
} else if (/* channelMask != 0 && */ mChannelIndexMask != 0 &&
Integer.bitCount(channelMask) != Integer.bitCount(mChannelIndexMask)) {
@@ -867,7 +884,11 @@
* @throws java.lang.IllegalArgumentException
*/
public Builder setSampleRate(int sampleRate) throws IllegalArgumentException {
- if ((sampleRate <= 0) || (sampleRate > 192000)) {
+ // TODO Consider whether to keep the MIN and MAX range checks here.
+ // It is not necessary and poses the problem of defining the limits independently from
+ // native implementation or platform capabilities.
+ if (((sampleRate < SAMPLE_RATE_HZ_MIN) || (sampleRate > SAMPLE_RATE_HZ_MAX)) &&
+ sampleRate != SAMPLE_RATE_UNSPECIFIED) {
throw new IllegalArgumentException("Invalid sample rate " + sampleRate);
}
mSampleRate = sampleRate;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index dc534be..38e3b15 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3321,6 +3321,7 @@
* Used as a key for {@link #getProperty} to request the native or optimal output sample rate
* for this device's primary output stream, in decimal Hz.
*/
+ // FIXME Deprecate
public static final String PROPERTY_OUTPUT_SAMPLE_RATE =
"android.media.property.OUTPUT_SAMPLE_RATE";
@@ -3328,6 +3329,7 @@
* Used as a key for {@link #getProperty} to request the native or optimal output buffer size
* for this device's primary output stream, in decimal PCM frames.
*/
+ // FIXME Deprecate
public static final String PROPERTY_OUTPUT_FRAMES_PER_BUFFER =
"android.media.property.OUTPUT_FRAMES_PER_BUFFER";
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index e342385..8f6b178 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -57,10 +57,6 @@
// Constants
//--------------------
- /** Minimum value for sample rate */
- private static final int SAMPLE_RATE_HZ_MIN = 4000;
- /** Maximum value for sample rate */
- private static final int SAMPLE_RATE_HZ_MAX = 192000;
/**
* indicates AudioRecord state is not successfully initialized.
@@ -168,8 +164,9 @@
//--------------------
/**
* The audio data sampling rate in Hz.
+ * Never {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED}.
*/
- private int mSampleRate;
+ private int mSampleRate; // initialized by all constructors via audioParamCheck()
/**
* The number of input audio channels (1 is mono, 2 is stereo)
*/
@@ -251,6 +248,9 @@
* @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only
* rate that is guaranteed to work on all devices, but other rates such as 22050,
* 16000, and 11025 may work on some devices.
+ * {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value
+ * which is usually the sample rate of the source.
+ * {@link #getSampleRate()} can be used to retrieve the actual sample rate chosen.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_IN_MONO} and
* {@link AudioFormat#CHANNEL_IN_STEREO}. {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed
@@ -337,16 +337,9 @@
mAudioAttributes = attributes;
}
- int rate = 0;
- if ((format.getPropertySetMask()
- & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0)
- {
- rate = format.getSampleRate();
- } else {
- rate = AudioSystem.getPrimaryOutputSamplingRate();
- if (rate <= 0) {
- rate = 44100;
- }
+ int rate = format.getSampleRate();
+ if (rate == AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
+ rate = 0;
}
int encoding = AudioFormat.ENCODING_DEFAULT;
@@ -373,12 +366,13 @@
audioBuffSizeCheck(bufferSizeInBytes);
+ int[] sampleRate = new int[] {mSampleRate};
int[] session = new int[1];
session[0] = sessionId;
//TODO: update native initialization when information about hardware init failure
// due to capture device already open is available.
int initResult = native_setup( new WeakReference<AudioRecord>(this),
- mAudioAttributes, mSampleRate, mChannelMask, mChannelIndexMask,
+ mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,
mAudioFormat, mNativeBufferSizeInBytes,
session, ActivityThread.currentOpPackageName());
if (initResult != SUCCESS) {
@@ -386,6 +380,7 @@
return; // with mState == STATE_UNINITIALIZED
}
+ mSampleRate = sampleRate[0];
mSessionId = session[0];
mState = STATE_INITIALIZED;
@@ -623,6 +618,7 @@
return mask;
}
+
// postconditions:
// mRecordSource is valid
// mAudioFormat is valid
@@ -642,7 +638,9 @@
//--------------
// sample rate
- if ((sampleRateInHz < SAMPLE_RATE_HZ_MIN) || (sampleRateInHz > SAMPLE_RATE_HZ_MAX)) {
+ if ((sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN ||
+ sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) &&
+ sampleRateInHz != AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
throw new IllegalArgumentException(sampleRateInHz
+ "Hz is not a supported sample rate.");
}
@@ -714,7 +712,11 @@
// Getters
//--------------------
/**
- * Returns the configured audio data sample rate in Hz
+ * Returns the configured audio sink sample rate in Hz.
+ * The sink sample rate never changes after construction.
+ * If the constructor had a specific sample rate, then the sink sample rate is that value.
+ * If the constructor had {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED},
+ * then the sink sample rate is a route-dependent default value based on the source [sic].
*/
public int getSampleRate() {
return mSampleRate;
@@ -861,6 +863,7 @@
* See {@link #AudioRecord(int, int, int, int, int)} for more information on valid
* configuration values.
* @param sampleRateInHz the sample rate expressed in Hertz.
+ * {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} is not permitted.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_IN_MONO} and
* {@link AudioFormat#CHANNEL_IN_STEREO}
@@ -1708,7 +1711,7 @@
private native final int native_setup(Object audiorecord_this,
Object /*AudioAttributes*/ attributes,
- int sampleRate, int channelMask, int channelIndexMask, int audioFormat,
+ int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
int buffSizeInBytes, int[] sessionId, String opPackageName);
// TODO remove: implementation calls directly into implementation of native_release()
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 6fc2f87..708768c 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -91,11 +91,6 @@
*/
private static final float GAIN_MAX = 1.0f;
- /** Minimum value for sample rate */
- private static final int SAMPLE_RATE_HZ_MIN = 4000;
- /** Maximum value for sample rate */
- private static final int SAMPLE_RATE_HZ_MAX = 192000;
-
/** Maximum value for AudioTrack channel count
* @hide public for MediaCode only, do not un-hide or change to a numeric literal
*/
@@ -254,6 +249,7 @@
private final Looper mInitializationLooper;
/**
* The audio data source sampling rate in Hz.
+ * Never {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED}.
*/
private int mSampleRate; // initialized by all constructors via audioParamCheck()
/**
@@ -340,6 +336,9 @@
* {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC},
* {@link AudioManager#STREAM_ALARM}, and {@link AudioManager#STREAM_NOTIFICATION}.
* @param sampleRateInHz the initial source sample rate expressed in Hz.
+ * {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value
+ * which is usually the sample rate of the sink.
+ * {@link #getSampleRate()} can be used to retrieve the actual sample rate chosen.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
@@ -389,6 +388,8 @@
* {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC},
* {@link AudioManager#STREAM_ALARM}, and {@link AudioManager#STREAM_NOTIFICATION}.
* @param sampleRateInHz the initial source sample rate expressed in Hz.
+ * {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value
+ * which is usually the sample rate of the sink.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
@@ -461,16 +462,11 @@
looper = Looper.getMainLooper();
}
- int rate = 0;
- if ((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0)
- {
- rate = format.getSampleRate();
- } else {
- rate = AudioSystem.getPrimaryOutputSamplingRate();
- if (rate <= 0) {
- rate = 44100;
- }
+ int rate = format.getSampleRate();
+ if (rate == AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
+ rate = 0;
}
+
int channelIndexMask = 0;
if ((format.getPropertySetMask()
& AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) != 0) {
@@ -503,17 +499,19 @@
throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);
}
+ int[] sampleRate = new int[] {mSampleRate};
int[] session = new int[1];
session[0] = sessionId;
// native initialization
int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
- mSampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
+ sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
mNativeBufferSizeInBytes, mDataLoadMode, session);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
}
+ mSampleRate = sampleRate[0];
mSessionId = session[0];
if (mDataLoadMode == MODE_STATIC) {
@@ -712,7 +710,7 @@
if (mFormat == null) {
mFormat = new AudioFormat.Builder()
.setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
- .setSampleRate(AudioSystem.getPrimaryOutputSamplingRate())
+ //.setSampleRate(AudioFormat.SAMPLE_RATE_UNSPECIFIED)
.setEncoding(AudioFormat.ENCODING_DEFAULT)
.build();
}
@@ -762,7 +760,9 @@
int audioFormat, int mode) {
//--------------
// sample rate, note these values are subject to change
- if (sampleRateInHz < SAMPLE_RATE_HZ_MIN || sampleRateInHz > SAMPLE_RATE_HZ_MAX) {
+ if ((sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN ||
+ sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) &&
+ sampleRateInHz != AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
throw new IllegalArgumentException(sampleRateInHz
+ "Hz is not a supported sample rate.");
}
@@ -948,7 +948,13 @@
}
/**
- * Returns the configured audio data sample rate in Hz
+ * Returns the configured audio source sample rate in Hz.
+ * The initial source sample rate depends on the constructor parameters,
+ * but the source sample rate may change if {@link #setPlaybackRate(int)} is called.
+ * If the constructor had a specific sample rate, then the initial sink sample rate is that
+ * value.
+ * If the constructor had {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED},
+ * then the initial sink sample rate is a route-dependent default value based on the source [sic].
*/
public int getSampleRate() {
return mSampleRate;
@@ -1218,6 +1224,7 @@
* to a higher value than the initial source sample rate, be sure to configure the buffer size
* based on the highest planned sample rate.
* @param sampleRateInHz the source sample rate expressed in Hz.
+ * {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} is not permitted.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
@@ -1255,7 +1262,9 @@
}
// sample rate, note these values are subject to change
- if ( (sampleRateInHz < SAMPLE_RATE_HZ_MIN) || (sampleRateInHz > SAMPLE_RATE_HZ_MAX) ) {
+ // Note: AudioFormat.SAMPLE_RATE_UNSPECIFIED is not allowed
+ if ( (sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN) ||
+ (sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) ) {
loge("getMinBufferSize(): " + sampleRateInHz + " Hz is not a supported sample rate.");
return ERROR_BAD_VALUE;
}
@@ -2763,7 +2772,7 @@
// AudioAttributes.USAGE_MEDIA will map to AudioManager.STREAM_MUSIC
private native final int native_setup(Object /*WeakReference<AudioTrack>*/ audiotrack_this,
Object /*AudioAttributes*/ attributes,
- int sampleRate, int channelMask, int channelIndexMask, int audioFormat,
+ int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
int buffSizeInBytes, int mode, int[] sessionId);
private native final void native_finalize();
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 4ffac6d..55fb82b 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -277,6 +277,7 @@
mRouteFlags = ROUTE_FLAG_RENDER;
}
if (mFormat == null) {
+ // FIXME Can we eliminate this? Will AudioMix work with an unspecified sample rate?
int rate = AudioSystem.getPrimaryOutputSamplingRate();
if (rate <= 0) {
rate = 44100;