diff --git a/api/current.txt b/api/current.txt index 67dd3659de53b..fec8b0602cc71 100644 --- a/api/current.txt +++ b/api/current.txt @@ -19767,6 +19767,7 @@ package android.media { 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 0629fe95181a6..b088f83710b6c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -21263,6 +21263,7 @@ package android.media { 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 ae539d813ba30..006dec40f4b32 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -19776,6 +19776,7 @@ package android.media { 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 d0326f159be04..6904fda1bef87 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -180,9 +180,13 @@ static sp setAudioRecord(JNIEnv* env, jobject thiz, const spGetIntArrayRegion(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 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, 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 @@ static const JNINativeMethod gMethods[] = { // 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 1ab95042a1867..84cc185d13e24 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -215,9 +215,13 @@ static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks( 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 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, 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 @@ static const JNINativeMethod gMethods[] = { {"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 22f4f04bcea8d..a0dfaa0f6fc69 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -335,6 +335,24 @@ public class AudioFormat implements Parcelable { 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 @@ public class AudioFormat implements Parcelable { } /** - * 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 @@ public class AudioFormat implements Parcelable { /** * 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 class AudioFormat implements Parcelable { */ 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 class AudioFormat implements Parcelable { 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 @@ public class AudioFormat implements Parcelable { * 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 @@ public class AudioFormat implements Parcelable { * @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 dc534be03cbbb..38e3b152b68d8 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -3321,6 +3321,7 @@ public class AudioManager { * 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 @@ public class AudioManager { * 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 e342385f4c6c2..8f6b17877fd47 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -57,10 +57,6 @@ public class AudioRecord implements AudioRouting // 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 @@ public class AudioRecord implements AudioRouting //-------------------- /** * 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 @@ public class AudioRecord implements AudioRouting * @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 @@ public class AudioRecord implements AudioRouting 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 @@ public class AudioRecord implements AudioRouting 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(this), - mAudioAttributes, mSampleRate, mChannelMask, mChannelIndexMask, + mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat, mNativeBufferSizeInBytes, session, ActivityThread.currentOpPackageName()); if (initResult != SUCCESS) { @@ -386,6 +380,7 @@ public class AudioRecord implements AudioRouting return; // with mState == STATE_UNINITIALIZED } + mSampleRate = sampleRate[0]; mSessionId = session[0]; mState = STATE_INITIALIZED; @@ -623,6 +618,7 @@ public class AudioRecord implements AudioRouting return mask; } + // postconditions: // mRecordSource is valid // mAudioFormat is valid @@ -642,7 +638,9 @@ public class AudioRecord implements AudioRouting //-------------- // 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 @@ public class AudioRecord implements AudioRouting // 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 @@ public class AudioRecord implements AudioRouting * 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 @@ public class AudioRecord implements AudioRouting 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 6fc2f87ee6404..708768cea19b8 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -91,11 +91,6 @@ public class AudioTrack implements AudioRouting */ 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 @@ public class AudioTrack implements AudioRouting 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 @@ public class AudioTrack implements AudioRouting * {@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 @@ public class AudioTrack implements AudioRouting * {@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 @@ public class AudioTrack implements AudioRouting 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 @@ public class AudioTrack implements AudioRouting 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(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 @@ public class AudioTrack implements AudioRouting 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 @@ public class AudioTrack implements AudioRouting 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 @@ public class AudioTrack implements AudioRouting } /** - * 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 @@ public class AudioTrack implements AudioRouting * 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 @@ public class AudioTrack implements AudioRouting } // 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 @@ public class AudioTrack implements AudioRouting // AudioAttributes.USAGE_MEDIA will map to AudioManager.STREAM_MUSIC private native final int native_setup(Object /*WeakReference*/ 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 4ffac6dd93bef..55fb82bbdcc53 100644 --- a/media/java/android/media/audiopolicy/AudioMix.java +++ b/media/java/android/media/audiopolicy/AudioMix.java @@ -277,6 +277,7 @@ public class AudioMix { 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;