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