Merge "Add floating point AudioRecord read"

This commit is contained in:
Andy Hung
2015-04-11 05:44:21 +00:00
committed by Android (Google) Code Review
4 changed files with 142 additions and 19 deletions

View File

@@ -14669,6 +14669,7 @@ package android.media {
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 @@ package android.media {
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

View File

@@ -15876,6 +15876,7 @@ package android.media {
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 @@ package android.media {
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

View File

@@ -440,6 +440,57 @@ static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thi
return (jint) readSize;
}
// ----------------------------------------------------------------------------
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) {
@@ -574,6 +625,8 @@ static JNINativeMethod gMethods[] = {
"([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},

View File

@@ -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 @@ public class AudioRecord
/** @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 @@ public class AudioRecord
* 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 @@ public class AudioRecord
* 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 @@ public class AudioRecord
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 @@ public class AudioRecord
// 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 @@ public class AudioRecord
}
/**
* 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 @@ public class AudioRecord
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 @@ public class AudioRecord
// 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 @@ public class AudioRecord
/**
* 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 @@ public class AudioRecord
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 @@ public class AudioRecord
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);