Merge "Add floating point AudioRecord read"
diff --git a/api/current.txt b/api/current.txt
index 8457968..e2ac046 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14669,6 +14669,7 @@
method public int getState();
method public int read(byte[], int, int);
method public int read(short[], int, int);
+ method public int read(float[], int, int, int);
method public int read(java.nio.ByteBuffer, int);
method public void release();
method public int setNotificationMarkerPosition(int);
@@ -14681,6 +14682,8 @@
field public static final int ERROR = -1; // 0xffffffff
field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
+ field public static final int READ_BLOCKING = 0; // 0x0
+ field public static final int READ_NON_BLOCKING = 1; // 0x1
field public static final int RECORDSTATE_RECORDING = 3; // 0x3
field public static final int RECORDSTATE_STOPPED = 1; // 0x1
field public static final int STATE_INITIALIZED = 1; // 0x1
diff --git a/api/system-current.txt b/api/system-current.txt
index f70f705..162a37c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -15876,6 +15876,7 @@
method public int getState();
method public int read(byte[], int, int);
method public int read(short[], int, int);
+ method public int read(float[], int, int, int);
method public int read(java.nio.ByteBuffer, int);
method public void release();
method public int setNotificationMarkerPosition(int);
@@ -15888,6 +15889,8 @@
field public static final int ERROR = -1; // 0xffffffff
field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
+ field public static final int READ_BLOCKING = 0; // 0x0
+ field public static final int READ_NON_BLOCKING = 1; // 0x1
field public static final int RECORDSTATE_RECORDING = 3; // 0x3
field public static final int RECORDSTATE_STOPPED = 1; // 0x1
field public static final int STATE_INITIALIZED = 1; // 0x1
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index b2e8f16..ec2b590 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -441,6 +441,57 @@
}
// ----------------------------------------------------------------------------
+static jint android_media_AudioRecord_readInFloatArray(JNIEnv *env, jobject thiz,
+ jfloatArray javaAudioData,
+ jint offsetInFloats, jint sizeInFloats,
+ jboolean isReadBlocking) {
+ // get the audio recorder from which we'll read new audio samples
+ sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+ if (lpRecorder == NULL) {
+ ALOGE("Unable to retrieve AudioRecord object");
+ return (jint)AUDIO_JAVA_INVALID_OPERATION;
+ }
+
+ if (javaAudioData == NULL) {
+ ALOGE("Invalid Java array to store recorded audio");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ // get the pointer to where we'll record the audio
+ // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
+ // a way that it becomes much more efficient. When doing so, we will have to prevent the
+ // AudioSystem callback to be called while in critical section (in case of media server
+ // process crash for instance)
+ jfloat *recordBuff = (jfloat *)env->GetFloatArrayElements(javaAudioData, NULL);
+ if (recordBuff == NULL) {
+ ALOGE("Error retrieving destination for recorded audio data");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ // read the new audio data from the native AudioRecord object
+ const size_t sizeInBytes = sizeInFloats * sizeof(float);
+ ssize_t readSize = lpRecorder->read(recordBuff + offsetInFloats, sizeInBytes);
+
+ env->ReleaseFloatArrayElements(javaAudioData, recordBuff, 0);
+
+ if (readSize < 0) {
+ ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize);
+ switch (readSize) {
+ case WOULD_BLOCK:
+ return (jint)0;
+ case BAD_VALUE:
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ default:
+ // may be possible for other errors such as
+ // NO_INIT to happen if restoreRecord_l fails.
+ case INVALID_OPERATION:
+ return (jint)AUDIO_JAVA_INVALID_OPERATION;
+ }
+ }
+ return (jint)(readSize / sizeof(float));
+}
+
+// ----------------------------------------------------------------------------
static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
jobject jBuffer, jint sizeInBytes) {
// get the audio recorder from which we'll read new audio samples
@@ -574,6 +625,8 @@
"([BII)I", (void *)android_media_AudioRecord_readInByteArray},
{"native_read_in_short_array",
"([SII)I", (void *)android_media_AudioRecord_readInShortArray},
+ {"native_read_in_float_array",
+ "([FIIZ)I", (void *)android_media_AudioRecord_readInFloatArray},
{"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
(void *)android_media_AudioRecord_readInDirectBuffer},
{"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 99b7bee..b7a851f 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,10 +16,13 @@
package android.media;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.Iterator;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Binder;
@@ -109,6 +112,26 @@
/** @hide */
public final static String SUBMIX_FIXED_VOLUME = "fixedVolume";
+ /** @hide */
+ @IntDef({
+ READ_BLOCKING,
+ READ_NON_BLOCKING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ReadMode {}
+
+ /**
+ * The read mode indicating the read operation will block until all data
+ * requested has been read.
+ */
+ public final static int READ_BLOCKING = 0;
+
+ /**
+ * The read mode indicating the read operation will return immediately after
+ * reading as much audio data as possible without blocking.
+ */
+ public final static int READ_NON_BLOCKING = 1;
+
//---------------------------------------------------------
// Used exclusively by native code
//--------------------
@@ -144,6 +167,7 @@
* The encoding of the audio samples.
* @see AudioFormat#ENCODING_PCM_8BIT
* @see AudioFormat#ENCODING_PCM_16BIT
+ * @see AudioFormat#ENCODING_PCM_FLOAT
*/
private int mAudioFormat;
/**
@@ -212,9 +236,9 @@
* See {@link AudioFormat#CHANNEL_IN_MONO} and
* {@link AudioFormat#CHANNEL_IN_STEREO}. {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed
* to work on all devices.
- * @param audioFormat the format in which the audio data is represented.
- * See {@link AudioFormat#ENCODING_PCM_16BIT} and
- * {@link AudioFormat#ENCODING_PCM_8BIT}
+ * @param audioFormat the format in which the audio data is to be returned.
+ * See {@link AudioFormat#ENCODING_PCM_8BIT}, {@link AudioFormat#ENCODING_PCM_16BIT},
+ * and {@link AudioFormat#ENCODING_PCM_FLOAT}.
* @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. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
@@ -559,13 +583,14 @@
case AudioFormat.ENCODING_DEFAULT:
mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
break;
+ case AudioFormat.ENCODING_PCM_FLOAT:
case AudioFormat.ENCODING_PCM_16BIT:
case AudioFormat.ENCODING_PCM_8BIT:
mAudioFormat = audioFormat;
break;
default:
throw new IllegalArgumentException("Unsupported sample encoding."
- + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT.");
+ + " Should be ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, or ENCODING_PCM_FLOAT.");
}
}
@@ -573,7 +598,8 @@
// Convenience method for the contructor's audio buffer size check.
// preconditions:
// mChannelCount is valid
- // mAudioFormat is AudioFormat.ENCODING_PCM_8BIT OR AudioFormat.ENCODING_PCM_16BIT
+ // mAudioFormat is AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT,
+ // or AudioFormat.ENCODING_PCM_FLOAT
// postcondition:
// mNativeBufferSizeInBytes is valid (multiple of frame size, positive)
private void audioBuffSizeCheck(int audioBufferSize) throws IllegalArgumentException {
@@ -632,8 +658,8 @@
}
/**
- * 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_8BIT},
+ * {@link AudioFormat#ENCODING_PCM_16BIT}, and {@link AudioFormat#ENCODING_PCM_FLOAT}.
*/
public int getAudioFormat() {
return mAudioFormat;
@@ -732,12 +758,6 @@
return ERROR_BAD_VALUE;
}
- // PCM_8BIT is not supported at the moment
- if (audioFormat != AudioFormat.ENCODING_PCM_16BIT) {
- loge("getMinBufferSize(): Invalid audio format.");
- return ERROR_BAD_VALUE;
- }
-
int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
if (size == 0) {
return ERROR_BAD_VALUE;
@@ -841,11 +861,13 @@
// Audio data supply
//--------------------
/**
- * Reads audio data from the audio hardware for recording into a buffer.
+ * Reads audio data from the audio hardware for recording into a byte array.
+ * The format specified in the AudioRecord constructor should be
+ * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
* @param audioData the array to which the recorded audio data is written.
* @param offsetInBytes index in audioData from which the data is written expressed in bytes.
* @param sizeInBytes the number of requested bytes.
- * @return the number of bytes that were read or or {@link #ERROR_INVALID_OPERATION}
+ * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes.
* The number of bytes will not exceed sizeInBytes.
@@ -866,11 +888,13 @@
/**
- * Reads audio data from the audio hardware for recording into a buffer.
+ * Reads audio data from the audio hardware for recording into a short array.
+ * The format specified in the AudioRecord constructor should be
+ * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array.
* @param audioData the array to which the recorded audio data is written.
* @param offsetInShorts index in audioData from which the data is written expressed in shorts.
* @param sizeInShorts the number of requested shorts.
- * @return the number of shorts that were read or or {@link #ERROR_INVALID_OPERATION}
+ * @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes.
* The number of shorts will not exceed sizeInShorts.
@@ -889,18 +913,55 @@
return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts);
}
+ /**
+ * Reads audio data from the audio hardware for recording into a float array.
+ * The format specified in the AudioRecord constructor should be
+ * {@link AudioFormat#ENCODING_PCM_FLOAT} to correspond to the data in the array.
+ * @param audioData the array to which the recorded audio data is written.
+ * @param offsetInFloats index in audioData from which the data is written.
+ * @param sizeInFloats the number of requested floats.
+ * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
+ * <BR>With {@link #READ_BLOCKING}, the read will block until all the requested data
+ * is read.
+ * <BR>With {@link #READ_NON_BLOCKING}, the read will return immediately after
+ * reading as much audio data as possible without blocking.
+ * @return the number of floats that were read or {@link #ERROR_INVALID_OPERATION}
+ * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+ * the parameters don't resolve to valid data and indexes.
+ * The number of floats will not exceed sizeInFloats.
+ */
+ public int read(float[] audioData, int offsetInFloats, int sizeInFloats,
+ @ReadMode int readMode) {
+ if (mState != STATE_INITIALIZED) {
+ return ERROR_INVALID_OPERATION;
+ }
+
+ if ( (audioData == null) || (offsetInFloats < 0 ) || (sizeInFloats < 0)
+ || (offsetInFloats + sizeInFloats < 0) // detect integer overflow
+ || (offsetInFloats + sizeInFloats > audioData.length)) {
+ return ERROR_BAD_VALUE;
+ }
+
+ return native_read_in_float_array(audioData, offsetInFloats, sizeInFloats,
+ readMode == READ_BLOCKING);
+ }
/**
* 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.
* Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is
* unchanged after a call to this method.
+ * The representation of the data in the buffer will depend on the format specified in
+ * the AudioRecord constructor, and will be native endian.
* @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 or or {@link #ERROR_INVALID_OPERATION}
+ * @param sizeInBytes the number of requested bytes. It is recommended but not enforced
+ * that the number of bytes requested be a multiple of the frame size (sample size in
+ * bytes multiplied by the channel count).
+ * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes.
* The number of bytes will not exceed sizeInBytes.
+ * The number of bytes read will truncated to be a multiple of the frame size.
*/
public int read(ByteBuffer audioBuffer, int sizeInBytes) {
if (mState != STATE_INITIALIZED) {
@@ -1101,6 +1162,9 @@
private native final int native_read_in_short_array(short[] audioData,
int offsetInShorts, int sizeInShorts);
+ private native final int native_read_in_float_array(float[] audioData,
+ int offsetInFloats, int sizeInFloats, boolean isBlocking);
+
private native final int native_read_in_direct_buffer(Object jBuffer, int sizeInBytes);
private native final int native_set_marker_pos(int marker);