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

Commit 58b0f3f3 authored by Andy Hung's avatar Andy Hung
Browse files

Add floating point AudioRecord read

Change-Id: Ibbdbfdefb84b515c231723fb621662bc4920ff86
parent eb9a10c8
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -14621,6 +14621,7 @@ package android.media {
    method public int getState();
    method public int getState();
    method public int read(byte[], int, int);
    method public int read(byte[], int, int);
    method public int read(short[], 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 int read(java.nio.ByteBuffer, int);
    method public void release();
    method public void release();
    method public int setNotificationMarkerPosition(int);
    method public int setNotificationMarkerPosition(int);
@@ -14633,6 +14634,8 @@ package android.media {
    field public static final int ERROR = -1; // 0xffffffff
    field public static final int ERROR = -1; // 0xffffffff
    field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
    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 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_RECORDING = 3; // 0x3
    field public static final int RECORDSTATE_STOPPED = 1; // 0x1
    field public static final int RECORDSTATE_STOPPED = 1; // 0x1
    field public static final int STATE_INITIALIZED = 1; // 0x1
    field public static final int STATE_INITIALIZED = 1; // 0x1
+3 −0
Original line number Original line Diff line number Diff line
@@ -15823,6 +15823,7 @@ package android.media {
    method public int getState();
    method public int getState();
    method public int read(byte[], int, int);
    method public int read(byte[], int, int);
    method public int read(short[], 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 int read(java.nio.ByteBuffer, int);
    method public void release();
    method public void release();
    method public int setNotificationMarkerPosition(int);
    method public int setNotificationMarkerPosition(int);
@@ -15835,6 +15836,8 @@ package android.media {
    field public static final int ERROR = -1; // 0xffffffff
    field public static final int ERROR = -1; // 0xffffffff
    field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
    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 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_RECORDING = 3; // 0x3
    field public static final int RECORDSTATE_STOPPED = 1; // 0x1
    field public static final int RECORDSTATE_STOPPED = 1; // 0x1
    field public static final int STATE_INITIALIZED = 1; // 0x1
    field public static final int STATE_INITIALIZED = 1; // 0x1
+53 −0
Original line number Original line Diff line number Diff line
@@ -440,6 +440,57 @@ static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thi
    return (jint) readSize;
    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,
static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
                                                  jobject jBuffer, jint sizeInBytes) {
                                                  jobject jBuffer, jint sizeInBytes) {
@@ -574,6 +625,8 @@ static JNINativeMethod gMethods[] = {
                             "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
                             "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
    {"native_read_in_short_array",
    {"native_read_in_short_array",
                             "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
                             "([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",
    {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
                                       (void *)android_media_AudioRecord_readInDirectBuffer},
                                       (void *)android_media_AudioRecord_readInDirectBuffer},
    {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
    {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
+83 −19
Original line number Original line Diff line number Diff line
@@ -16,10 +16,13 @@


package android.media;
package android.media;


import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.Iterator;


import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.SystemApi;
import android.os.Binder;
import android.os.Binder;
@@ -109,6 +112,26 @@ public class AudioRecord
    /** @hide */
    /** @hide */
    public final static String SUBMIX_FIXED_VOLUME = "fixedVolume";
    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
    // Used exclusively by native code
    //--------------------
    //--------------------
@@ -144,6 +167,7 @@ public class AudioRecord
     * The encoding of the audio samples.
     * The encoding of the audio samples.
     * @see AudioFormat#ENCODING_PCM_8BIT
     * @see AudioFormat#ENCODING_PCM_8BIT
     * @see AudioFormat#ENCODING_PCM_16BIT
     * @see AudioFormat#ENCODING_PCM_16BIT
     * @see AudioFormat#ENCODING_PCM_FLOAT
     */
     */
    private int mAudioFormat;
    private int mAudioFormat;
    /**
    /**
@@ -212,9 +236,9 @@ public class AudioRecord
     *   See {@link AudioFormat#CHANNEL_IN_MONO} and
     *   See {@link AudioFormat#CHANNEL_IN_MONO} and
     *   {@link AudioFormat#CHANNEL_IN_STEREO}.  {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed
     *   {@link AudioFormat#CHANNEL_IN_STEREO}.  {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed
     *   to work on all devices.
     *   to work on all devices.
     * @param audioFormat the format in which the audio data is represented.
     * @param audioFormat the format in which the audio data is to be returned.
     *   See {@link AudioFormat#ENCODING_PCM_16BIT} and
     *   See {@link AudioFormat#ENCODING_PCM_8BIT}, {@link AudioFormat#ENCODING_PCM_16BIT},
     *   {@link AudioFormat#ENCODING_PCM_8BIT}
     *   and {@link AudioFormat#ENCODING_PCM_FLOAT}.
     * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written
     * @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
     *   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
     *   than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
@@ -559,13 +583,14 @@ public class AudioRecord
        case AudioFormat.ENCODING_DEFAULT:
        case AudioFormat.ENCODING_DEFAULT:
            mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
            mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
            break;
            break;
        case AudioFormat.ENCODING_PCM_FLOAT:
        case AudioFormat.ENCODING_PCM_16BIT:
        case AudioFormat.ENCODING_PCM_16BIT:
        case AudioFormat.ENCODING_PCM_8BIT:
        case AudioFormat.ENCODING_PCM_8BIT:
            mAudioFormat = audioFormat;
            mAudioFormat = audioFormat;
            break;
            break;
        default:
        default:
            throw new IllegalArgumentException("Unsupported sample encoding."
            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.
    // Convenience method for the contructor's audio buffer size check.
    // preconditions:
    // preconditions:
    //    mChannelCount is valid
    //    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:
    // postcondition:
    //    mNativeBufferSizeInBytes is valid (multiple of frame size, positive)
    //    mNativeBufferSizeInBytes is valid (multiple of frame size, positive)
    private void audioBuffSizeCheck(int audioBufferSize) throws IllegalArgumentException {
    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}
     * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_8BIT},
     * and {@link AudioFormat#ENCODING_PCM_8BIT}.
     * {@link AudioFormat#ENCODING_PCM_16BIT}, and {@link AudioFormat#ENCODING_PCM_FLOAT}.
     */
     */
    public int getAudioFormat() {
    public int getAudioFormat() {
        return mAudioFormat;
        return mAudioFormat;
@@ -732,12 +758,6 @@ public class AudioRecord
            return ERROR_BAD_VALUE;
            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);
        int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
        if (size == 0) {
        if (size == 0) {
            return ERROR_BAD_VALUE;
            return ERROR_BAD_VALUE;
@@ -841,11 +861,13 @@ public class AudioRecord
    // Audio data supply
    // 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 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 offsetInBytes index in audioData from which the data is written expressed in bytes.
     * @param sizeInBytes the number of requested 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
     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
     *    the parameters don't resolve to valid data and indexes.
     *    the parameters don't resolve to valid data and indexes.
     *    The number of bytes will not exceed sizeInBytes.
     *    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 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 offsetInShorts index in audioData from which the data is written expressed in shorts.
     * @param sizeInShorts the number of requested 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
     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
     *    the parameters don't resolve to valid data and indexes.
     *    the parameters don't resolve to valid data and indexes.
     *    The number of shorts will not exceed sizeInShorts.
     *    The number of shorts will not exceed sizeInShorts.
@@ -889,18 +913,55 @@ public class AudioRecord
        return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts);
        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
     * 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.
     * 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
     * Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is
     * unchanged after a call to this method.
     * 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 audioBuffer the direct buffer to which the recorded audio data is written.
     * @param sizeInBytes the number of requested bytes.
     * @param sizeInBytes the number of requested bytes. It is recommended but not enforced
     * @return the number of bytes that were read or or {@link #ERROR_INVALID_OPERATION}
     *    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
     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
     *    the parameters don't resolve to valid data and indexes.
     *    the parameters don't resolve to valid data and indexes.
     *    The number of bytes will not exceed sizeInBytes.
     *    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) {
    public int read(ByteBuffer audioBuffer, int sizeInBytes) {
        if (mState != STATE_INITIALIZED) {
        if (mState != STATE_INITIALIZED) {
@@ -1101,6 +1162,9 @@ public class AudioRecord
    private native final int native_read_in_short_array(short[] audioData,
    private native final int native_read_in_short_array(short[] audioData,
            int offsetInShorts, int sizeInShorts);
            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_read_in_direct_buffer(Object jBuffer, int sizeInBytes);


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