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

Commit 505e5c88 authored by Eric Laurent's avatar Eric Laurent
Browse files

implemented synchronous audio capture

Added the infrastructure to support the synchronization of playback and
capture actions on specific events.
The first requirement for this feature is to synchronize the audio capture
start with the full rendering of a given audio content.
The applications can further be extended to other use cases
(synchronized playback start...) by adding new synchronization events and
new synchronous control methods on player or recorders.

Also added a method to query the audio session from a ToneGenerator.

Change-Id: I4e47f5108c7cbbd3bd334a7fad9b3b6c5ba55d88
parent f31af709
Loading
Loading
Loading
Loading
+4 −3
Original line number Original line Diff line number Diff line
@@ -254,7 +254,7 @@ native_track_failure:


// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static int
static int
android_media_AudioRecord_start(JNIEnv *env, jobject thiz)
android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
{
{
    AudioRecord *lpRecorder =
    AudioRecord *lpRecorder =
            (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
            (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
@@ -263,7 +263,8 @@ android_media_AudioRecord_start(JNIEnv *env, jobject thiz)
        return AUDIORECORD_ERROR;
        return AUDIORECORD_ERROR;
    }
    }


    return android_media_translateRecorderErrorCode(lpRecorder->start());
    return android_media_translateRecorderErrorCode(
            lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
}
}




@@ -508,7 +509,7 @@ static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject th
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
static JNINativeMethod gMethods[] = {
    // name,               signature,  funcPtr
    // name,               signature,  funcPtr
    {"native_start",         "()I",    (void *)android_media_AudioRecord_start},
    {"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},
    {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
    {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
    {"native_setup",         "(Ljava/lang/Object;IIIII[I)I",
    {"native_setup",         "(Ljava/lang/Object;IIIII[I)I",
                                       (void *)android_media_AudioRecord_setup},
                                       (void *)android_media_AudioRecord_setup},
+11 −0
Original line number Original line Diff line number Diff line
@@ -65,6 +65,16 @@ static void android_media_ToneGenerator_stopTone(JNIEnv *env, jobject thiz) {
    lpToneGen->stopTone();
    lpToneGen->stopTone();
}
}


static jint android_media_ToneGenerator_getAudioSessionId(JNIEnv *env, jobject thiz) {
    ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz,
            fields.context);
    if (lpToneGen == NULL) {
        jniThrowRuntimeException(env, "Method called after release()");
        return 0;
    }
    return lpToneGen->getSessionId();
}

static void android_media_ToneGenerator_release(JNIEnv *env, jobject thiz) {
static void android_media_ToneGenerator_release(JNIEnv *env, jobject thiz) {
    ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz,
    ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz,
            fields.context);
            fields.context);
@@ -120,6 +130,7 @@ static void android_media_ToneGenerator_native_finalize(JNIEnv *env,
static JNINativeMethod gMethods[] = {
static JNINativeMethod gMethods[] = {
    { "startTone", "(II)Z", (void *)android_media_ToneGenerator_startTone },
    { "startTone", "(II)Z", (void *)android_media_ToneGenerator_startTone },
    { "stopTone", "()V", (void *)android_media_ToneGenerator_stopTone },
    { "stopTone", "()V", (void *)android_media_ToneGenerator_stopTone },
    { "getAudioSessionId", "()I", (void *)android_media_ToneGenerator_getAudioSessionId},
    { "release", "()V", (void *)android_media_ToneGenerator_release },
    { "release", "()V", (void *)android_media_ToneGenerator_release },
    { "native_setup", "(II)V", (void *)android_media_ToneGenerator_native_setup },
    { "native_setup", "(II)V", (void *)android_media_ToneGenerator_native_setup },
    { "native_finalize", "()V", (void *)android_media_ToneGenerator_native_finalize }
    { "native_finalize", "()V", (void *)android_media_ToneGenerator_native_finalize }
+23 −2
Original line number Original line Diff line number Diff line
@@ -519,13 +519,34 @@ public class AudioRecord


        // start recording
        // start recording
        synchronized(mRecordingStateLock) {
        synchronized(mRecordingStateLock) {
            if (native_start() == SUCCESS) {
            if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) {
                mRecordingState = RECORDSTATE_RECORDING;
                mRecordingState = RECORDSTATE_RECORDING;
            }
            }
        }
        }
    }
    }


    /**
     * Starts recording from the AudioRecord instance when the specified synchronization event
     * occurs on the specified audio session.
     * @throws IllegalStateException
     * @param syncEvent event that triggers the capture.
     * @see MediaSyncEvent
     * @hide
     */
    public void startRecording(MediaSyncEvent syncEvent)
    throws IllegalStateException {
        if (mState != STATE_INITIALIZED) {
            throw(new IllegalStateException("startRecording() called on an "
                    +"uninitialized AudioRecord."));
        }


        // start recording
        synchronized(mRecordingStateLock) {
            if (native_start(syncEvent.getType(), syncEvent.getAudioSessionId()) == SUCCESS) {
                mRecordingState = RECORDSTATE_RECORDING;
            }
        }
    }


    /**
    /**
     * Stops recording.
     * Stops recording.
@@ -787,7 +808,7 @@ public class AudioRecord
    
    
    private native final void native_release();
    private native final void native_release();


    private native final int native_start();
    private native final int native_start(int syncEvent, int sessionId);


    private native final void native_stop();
    private native final void native_stop();


+4 −0
Original line number Original line Diff line number Diff line
@@ -311,6 +311,10 @@ public class AudioSystem
    public static final int FOR_DOCK = 3;
    public static final int FOR_DOCK = 3;
    private static final int NUM_FORCE_USE = 4;
    private static final int NUM_FORCE_USE = 4;


    // usage for AudioRecord.startRecordingSync(), must match AudioSystem::sync_event_t
    public static final int SYNC_EVENT_NONE = 0;
    public static final int SYNC_EVENT_PRESENTATION_COMPLETE = 1;

    public static native int setDeviceConnectionState(int device, int state, String device_address);
    public static native int setDeviceConnectionState(int device, int state, String device_address);
    public static native int getDeviceConnectionState(int device, String device_address);
    public static native int getDeviceConnectionState(int device, String device_address);
    public static native int setPhoneState(int state);
    public static native int setPhoneState(int state);
+123 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

/**
 * The MediaSyncEvent class defines events that can be used to synchronize playback or capture
 * actions between different players and recorders.
 * <p>For instance, {@link AudioRecord#startRecording(MediaSyncEvent)} is used to start capture
 * only when the playback on a particular audio session is complete.
 * The audio session ID is retrieved from a player (e.g {@link MediaPlayer}, {@link AudioTrack} or
 * {@link ToneGenerator}) by use of the getAudioSessionId() method.
 * @hide
 */
public class MediaSyncEvent {

    /**
     * No sync event specified. When used with a synchronized playback or capture method, the
     * behavior is equivalent to calling the corresponding non synchronized method.
     */
    public static final int SYNC_EVENT_NONE = AudioSystem.SYNC_EVENT_NONE;

    /**
     * The corresponding action is triggered only when the presentation is completed
     * (meaning the media has been presented to the user) on the specified session.
     * A synchronization of this type requires a source audio session ID to be set via
     * {@link #setAudioSessionId(int) method.
     */
    public static final int SYNC_EVENT_PRESENTATION_COMPLETE =
                                                    AudioSystem.SYNC_EVENT_PRESENTATION_COMPLETE;


    /**
     * Creates a synchronization event of the sepcified type.
     *
     * <p>The type specifies which kind of event is monitored.
     * For instance, event {@link #SYNC_EVENT_PRESENTATION_COMPLETE} corresponds to the audio being
     * presented to the user on a particular audio session.
     * @param type the synchronization event type.
     * @return the MediaSyncEvent created.
     * @throws java.lang.IllegalArgumentException
     */
    public static MediaSyncEvent createEvent(int eventType)
                            throws IllegalArgumentException {
        if (!isValidType(eventType)) {
            throw (new IllegalArgumentException(eventType
                    + "is not a valid MediaSyncEvent type."));
        } else {
            return new MediaSyncEvent(eventType);
        }
    }

    private final int mType;
    private int mAudioSession = 0;

    private MediaSyncEvent(int eventType) {
        mType = eventType;
    }

    /**
     * Sets the event source audio session ID.
     *
     * <p>The audio session ID specifies on which audio session the synchronization event should be
     * monitored.
     * It is mandatory for certain event types (e.g. {@link #SYNC_EVENT_PRESENTATION_COMPLETE}).
     * For instance, the audio session ID can be retrieved via
     * {@link MediaPlayer#getAudioSessionId()} when monitoring an event on a particular MediaPlayer.
     * @param audioSessionId the audio session ID of the event source being monitored.
     * @return the MediaSyncEvent the method is called on.
     * @throws java.lang.IllegalArgumentException
     */
    public MediaSyncEvent setAudioSessionId(int audioSessionId)
            throws IllegalArgumentException {
        if (audioSessionId > 0) {
            mAudioSession = audioSessionId;
        } else {
            throw (new IllegalArgumentException(audioSessionId + " is not a valid session ID."));
        }
        return this;
    }

    /**
     * Gets the synchronization event type.
     *
     * @return the synchronization event type.
     */
    public int getType() {
        return mType;
    }

    /**
     * Gets the synchronization event audio session ID.
     *
     * @return the synchronization audio session ID. The returned audio session ID is 0 if it has
     * not been set.
     */
    public int getAudioSessionId() {
        return mAudioSession;
    }

    private static boolean isValidType(int type) {
        switch (type) {
        case SYNC_EVENT_NONE:
        case SYNC_EVENT_PRESENTATION_COMPLETE:
            return true;
        default:
            return false;
        }
    }
}
Loading