1a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// found in the LICENSE file.
4a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
5a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)package org.chromium.media;
6a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.annotation.SuppressLint;
8a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import android.media.AudioFormat;
9a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import android.media.AudioRecord;
10a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import android.media.MediaRecorder.AudioSource;
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.media.audiofx.AcousticEchoCanceler;
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.media.audiofx.AudioEffect;
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.media.audiofx.AudioEffect.Descriptor;
14a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import android.os.Process;
15a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import android.util.Log;
16a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
17a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import org.chromium.base.CalledByNative;
18a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import org.chromium.base.JNINamespace;
19a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
20a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import java.nio.ByteBuffer;
21a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
22a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Owned by its native counterpart declared in audio_record_input.h. Refer to
23a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// that class for general comments.
24a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)@JNINamespace("media")
25a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)class AudioRecordInput {
26a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private static final String TAG = "AudioRecordInput";
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Set to true to enable debug logs. Always check in as false.
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private static final boolean DEBUG = false;
29a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // We are unable to obtain a precise measurement of the hardware delay on
30a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // Android. This is a conservative lower-bound based on measurments. It
31a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // could surely be tightened with further testing.
32a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private static final int HARDWARE_DELAY_MS = 100;
33a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
34a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private final long mNativeAudioRecordInputStream;
35a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private final int mSampleRate;
36a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private final int mChannels;
37a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private final int mBitsPerSample;
38a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private final int mHardwareDelayBytes;
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private final boolean mUsePlatformAEC;
40a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private ByteBuffer mBuffer;
41a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private AudioRecord mAudioRecord;
42a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private AudioRecordThread mAudioRecordThread;
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private AcousticEchoCanceler mAEC;
44a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
45a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private class AudioRecordThread extends Thread {
46a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // The "volatile" synchronization technique is discussed here:
47a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // http://stackoverflow.com/a/106787/299268
48a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // and more generally in this article:
49a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // https://www.ibm.com/developerworks/java/library/j-jtp06197/
50a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        private volatile boolean mKeepAlive = true;
51a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
52a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        @Override
53a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        public void run() {
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            try {
56a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                mAudioRecord.startRecording();
57a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            } catch (IllegalStateException e) {
58a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                Log.e(TAG, "startRecording failed", e);
59a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                return;
60a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            }
61a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            while (mKeepAlive) {
63a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                int bytesRead = mAudioRecord.read(mBuffer, mBuffer.capacity());
64a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                if (bytesRead > 0) {
65a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                    nativeOnData(mNativeAudioRecordInputStream, bytesRead,
66a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                 mHardwareDelayBytes);
67a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                } else {
68a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                    Log.e(TAG, "read failed: " + bytesRead);
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    if (bytesRead == AudioRecord.ERROR_INVALID_OPERATION) {
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        // This can happen if there is already an active
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        // AudioRecord (e.g. in another tab).
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        mKeepAlive = false;
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    }
74a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                }
75a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            }
76a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
77a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            try {
78a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                mAudioRecord.stop();
79a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            } catch (IllegalStateException e) {
80a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                Log.e(TAG, "stop failed", e);
81a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            }
82a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
83a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
84a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        public void joinRecordThread() {
85a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            mKeepAlive = false;
86a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            while (isAlive()) {
87a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                try {
88a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                    join();
89a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                } catch (InterruptedException e) {
90a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                    // Ignore.
91a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                }
92a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            }
93a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
94a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
95a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
96a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    @CalledByNative
97a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private static AudioRecordInput createAudioRecordInput(long nativeAudioRecordInputStream,
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            int sampleRate, int channels, int bitsPerSample, int bytesPerBuffer,
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            boolean usePlatformAEC) {
100a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        return new AudioRecordInput(nativeAudioRecordInputStream, sampleRate, channels,
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    bitsPerSample, bytesPerBuffer, usePlatformAEC);
102a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
103a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
104a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private AudioRecordInput(long nativeAudioRecordInputStream, int sampleRate, int channels,
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                             int bitsPerSample, int bytesPerBuffer, boolean usePlatformAEC) {
106a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        mNativeAudioRecordInputStream = nativeAudioRecordInputStream;
107a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        mSampleRate = sampleRate;
108a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        mChannels = channels;
109a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        mBitsPerSample = bitsPerSample;
110a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        mHardwareDelayBytes = HARDWARE_DELAY_MS * sampleRate / 1000 * bitsPerSample / 8;
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mUsePlatformAEC = usePlatformAEC;
112a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
113a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // We use a direct buffer so that the native class can have access to
114a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // the underlying memory address. This avoids the need to copy from a
115a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // jbyteArray to native memory. More discussion of this here:
116a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // http://developer.android.com/training/articles/perf-jni.html
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mBuffer = ByteBuffer.allocateDirect(bytesPerBuffer);
118a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // Rather than passing the ByteBuffer with every OnData call (requiring
119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // the potentially expensive GetDirectBufferAddress) we simply have the
120a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // the native class cache the address to the memory once.
121a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        //
122a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // Unfortunately, profiling with traceview was unable to either confirm
123a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // or deny the advantage of this approach, as the values for
124a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // nativeOnData() were not stable across runs.
125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        nativeCacheDirectBufferAddress(mNativeAudioRecordInputStream, mBuffer);
126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
127a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @SuppressLint("NewApi")
129a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    @CalledByNative
130a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private boolean open() {
131a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (mAudioRecord != null) {
132a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            Log.e(TAG, "open() called twice without a close()");
133a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            return false;
134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
135a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        int channelConfig;
136a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (mChannels == 1) {
137a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            channelConfig = AudioFormat.CHANNEL_IN_MONO;
138a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        } else if (mChannels == 2) {
139a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            channelConfig = AudioFormat.CHANNEL_IN_STEREO;
140a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        } else {
141a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            Log.e(TAG, "Unsupported number of channels: " + mChannels);
142a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            return false;
143a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
144a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
145a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        int audioFormat;
146a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (mBitsPerSample == 8) {
147a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            audioFormat = AudioFormat.ENCODING_PCM_8BIT;
148a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        } else if (mBitsPerSample == 16) {
149a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            audioFormat = AudioFormat.ENCODING_PCM_16BIT;
150a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        } else {
151a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            Log.e(TAG, "Unsupported bits per sample: " + mBitsPerSample);
152a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            return false;
153a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
154a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
155a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // TODO(ajm): Do we need to make this larger to avoid underruns? The
156a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // Android documentation notes "this size doesn't guarantee a smooth
157a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // recording under load".
158a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        int minBufferSize = AudioRecord.getMinBufferSize(mSampleRate, channelConfig, audioFormat);
159a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (minBufferSize < 0) {
160a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            Log.e(TAG, "getMinBufferSize error: " + minBufferSize);
161a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            return false;
162a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
163a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
164a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // We will request mBuffer.capacity() with every read call. The
165a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // underlying AudioRecord buffer should be at least this large.
166a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        int audioRecordBufferSizeInBytes = Math.max(mBuffer.capacity(), minBufferSize);
167a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        try {
168a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            // TODO(ajm): Allow other AudioSource types to be requested?
169a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            mAudioRecord = new AudioRecord(AudioSource.VOICE_COMMUNICATION,
170a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                           mSampleRate,
171a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                           channelConfig,
172a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                           audioFormat,
173a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                           audioRecordBufferSizeInBytes);
174a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        } catch (IllegalArgumentException e) {
175a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            Log.e(TAG, "AudioRecord failed", e);
176a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            return false;
177a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
178a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (AcousticEchoCanceler.isAvailable()) {
1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            mAEC = AcousticEchoCanceler.create(mAudioRecord.getAudioSessionId());
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if (mAEC == null) {
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                Log.e(TAG, "AcousticEchoCanceler.create failed");
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                return false;
1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            }
1855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            int ret = mAEC.setEnabled(mUsePlatformAEC);
1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if (ret != AudioEffect.SUCCESS) {
1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                Log.e(TAG, "setEnabled error: " + ret);
1885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                return false;
1895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            }
1905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if (DEBUG) {
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                Descriptor descriptor = mAEC.getDescriptor();
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                Log.d(TAG, "AcousticEchoCanceler " +
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        "name: " + descriptor.name + ", " +
1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        "implementor: " + descriptor.implementor + ", " +
1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        "uuid: " + descriptor.uuid);
1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            }
1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
199a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        return true;
200a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
201a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
202a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    @CalledByNative
203a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private void start() {
204a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (mAudioRecord == null) {
205a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            Log.e(TAG, "start() called before open().");
206a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            return;
207a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
208a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (mAudioRecordThread != null) {
209a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            // start() was already called.
210a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            return;
211a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
212a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        mAudioRecordThread = new AudioRecordThread();
213a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        mAudioRecordThread.start();
214a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
215a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
216a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    @CalledByNative
217a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private void stop() {
218a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (mAudioRecordThread == null) {
219a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            // start() was never called, or stop() was already called.
220a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            return;
221a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
222a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        mAudioRecordThread.joinRecordThread();
223a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        mAudioRecordThread = null;
224a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
225a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
2265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @SuppressLint("NewApi")
227a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    @CalledByNative
228a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private void close() {
229a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (mAudioRecordThread != null) {
230a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            Log.e(TAG, "close() called before stop().");
231a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            return;
232a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
233a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (mAudioRecord == null) {
234a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            // open() was not called.
235a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            return;
236a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
2375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (mAEC != null) {
2395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            mAEC.release();
2405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            mAEC = null;
2415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
242a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        mAudioRecord.release();
243a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        mAudioRecord = null;
244a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
245a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
246a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private native void nativeCacheDirectBufferAddress(long nativeAudioRecordInputStream,
247a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                                       ByteBuffer buffer);
248a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private native void nativeOnData(long nativeAudioRecordInputStream, int size,
249a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     int hardwareDelayBytes);
250a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
251