Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Comminos <andrew@comminos.com>2016-03-06 12:13:30 +0300
committerAndrew Comminos <andrew@comminos.com>2016-03-06 12:13:30 +0300
commitb678b2405dc22015c9dad80c0368bf8af62c9fde (patch)
tree4c72d74519b99fb70b3289b20daf344f938868c0
parent6af23db0e772fd67ba5c55aa0f9ac74ea9606153 (diff)
Keep audio input thread running on PTT, integrate polymorphic input modes into AudioInput.
-rw-r--r--src/main/java/com/morlunk/jumble/JumbleService.java119
-rw-r--r--src/main/java/com/morlunk/jumble/audio/AudioInput.java81
-rw-r--r--src/main/java/com/morlunk/jumble/audio/inputmode/ActivityInputMode.java12
-rw-r--r--src/main/java/com/morlunk/jumble/audio/inputmode/ContinuousInputMode.java2
-rw-r--r--src/main/java/com/morlunk/jumble/audio/inputmode/IInputMode.java2
-rw-r--r--src/main/java/com/morlunk/jumble/audio/inputmode/ToggleInputMode.java2
-rw-r--r--src/main/java/com/morlunk/jumble/protocol/AudioHandler.java170
7 files changed, 137 insertions, 251 deletions
diff --git a/src/main/java/com/morlunk/jumble/JumbleService.java b/src/main/java/com/morlunk/jumble/JumbleService.java
index ff05434..dce42df 100644
--- a/src/main/java/com/morlunk/jumble/JumbleService.java
+++ b/src/main/java/com/morlunk/jumble/JumbleService.java
@@ -35,6 +35,10 @@ import android.util.Log;
import com.morlunk.jumble.audio.AudioOutput;
import com.morlunk.jumble.audio.BluetoothScoReceiver;
+import com.morlunk.jumble.audio.inputmode.ActivityInputMode;
+import com.morlunk.jumble.audio.inputmode.ContinuousInputMode;
+import com.morlunk.jumble.audio.inputmode.IInputMode;
+import com.morlunk.jumble.audio.inputmode.ToggleInputMode;
import com.morlunk.jumble.audio.javacpp.CELT7;
import com.morlunk.jumble.exception.AudioException;
import com.morlunk.jumble.exception.NotConnectedException;
@@ -124,6 +128,7 @@ public class JumbleService extends Service implements IJumbleService, JumbleConn
private List<Integer> mLocalMuteHistory;
private List<Integer> mLocalIgnoreHistory;
private AudioHandler.Builder mAudioBuilder;
+ private int mTransmitMode;
private PowerManager.WakeLock mWakeLock;
private Handler mHandler;
@@ -135,6 +140,10 @@ public class JumbleService extends Service implements IJumbleService, JumbleConn
private AudioHandler mAudioHandler;
private BluetoothScoReceiver mBluetoothReceiver;
+ private ActivityInputMode mActivityInputMode;
+ private ToggleInputMode mToggleInputMode;
+ private ContinuousInputMode mContinuousInputMode;
+
private boolean mReconnecting;
/**
@@ -158,34 +167,38 @@ public class JumbleService extends Service implements IJumbleService, JumbleConn
private AudioHandler.AudioEncodeListener mAudioInputListener =
new AudioHandler.AudioEncodeListener() {
- @Override
- public void onAudioEncoded(byte[] data, int length) {
- if(mConnection != null && mConnection.isSynchronized()) {
- mConnection.sendUDPMessage(data, length, false);
- }
- }
-
- @Override
- public void onTalkStateChange(final TalkState state) {
- mHandler.post(new Runnable() {
@Override
- public void run() {
- try {
- if (!isSynchronized())
- throw new NotSynchronizedException();
-
- final User currentUser = mModelHandler.getUser(mConnection.getSession());
- if (currentUser == null) return;
-
- currentUser.setTalkState(state);
- mCallbacks.onUserTalkStateUpdated(currentUser);
- } catch (NotSynchronizedException e) {
- e.printStackTrace();
+ public void onAudioEncoded(byte[] data, int length) {
+ if(mConnection != null && mConnection.isSynchronized()) {
+ mConnection.sendUDPMessage(data, length, false);
}
}
- });
- }
- };
+
+ @Override
+ public void setTransmitting(final boolean talking) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (!isSynchronized())
+ throw new NotSynchronizedException();
+
+ final User currentUser = mModelHandler.getUser(mConnection.getSession());
+ if (currentUser == null) return;
+
+ // FIXME: should be changed to work for whispers.
+ if ((currentUser.getTalkState() == TalkState.TALKING) ^ talking) {
+ currentUser.setTalkState(talking ? TalkState.TALKING : TalkState.PASSIVE);
+ mCallbacks.onUserTalkStateUpdated(currentUser);
+
+ }
+ } catch (NotSynchronizedException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+ };
private AudioOutput.AudioOutputListener mAudioOutputListener = new AudioOutput.AudioOutputListener() {
@Override
@@ -241,6 +254,9 @@ public class JumbleService extends Service implements IJumbleService, JumbleConn
mConnectionState = ConnectionState.DISCONNECTED;
mBluetoothReceiver = new BluetoothScoReceiver(this, this);
registerReceiver(mBluetoothReceiver, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
+ mToggleInputMode = new ToggleInputMode();
+ mActivityInputMode = new ActivityInputMode(0); // FIXME: reasonable default
+ mContinuousInputMode = new ContinuousInputMode();
}
@Override
@@ -490,13 +506,28 @@ public class JumbleService extends Service implements IJumbleService, JumbleConn
reconnectNeeded = true;
}
if (extras.containsKey(EXTRAS_DETECTION_THRESHOLD)) {
- mAudioBuilder.setVADThreshold(extras.getFloat(EXTRAS_DETECTION_THRESHOLD));
+ mActivityInputMode.setThreshold(extras.getFloat(EXTRAS_DETECTION_THRESHOLD));
}
if (extras.containsKey(EXTRAS_AMPLITUDE_BOOST)) {
mAudioBuilder.setAmplitudeBoost(extras.getFloat(EXTRAS_AMPLITUDE_BOOST));
}
if (extras.containsKey(EXTRAS_TRANSMIT_MODE)) {
- mAudioBuilder.setTransmitMode(extras.getInt(EXTRAS_TRANSMIT_MODE));
+ mTransmitMode = extras.getInt(EXTRAS_TRANSMIT_MODE);
+ IInputMode inputMode;
+ switch (mTransmitMode) {
+ case Constants.TRANSMIT_PUSH_TO_TALK:
+ inputMode = mToggleInputMode;
+ break;
+ case Constants.TRANSMIT_CONTINUOUS:
+ inputMode = mContinuousInputMode;
+ break;
+ case Constants.TRANSMIT_VOICE_ACTIVITY:
+ inputMode = mActivityInputMode;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ mAudioBuilder.setInputMode(inputMode);
}
if (extras.containsKey(EXTRAS_INPUT_RATE)) {
mAudioBuilder.setInputSampleRate(extras.getInt(EXTRAS_INPUT_RATE));
@@ -549,7 +580,9 @@ public class JumbleService extends Service implements IJumbleService, JumbleConn
reconnectNeeded = true;
}
if (extras.containsKey(EXTRAS_HALF_DUPLEX)) {
- mAudioBuilder.setHalfDuplexEnabled(extras.getBoolean(EXTRAS_HALF_DUPLEX));
+ mAudioBuilder.setHalfDuplexEnabled(
+ extras.getInt(EXTRAS_TRANSMIT_MODE) == Constants.TRANSMIT_PUSH_TO_TALK
+ && extras.getBoolean(EXTRAS_HALF_DUPLEX));
}
if (extras.containsKey(EXTRAS_LOCAL_MUTE_HISTORY)) {
mLocalMuteHistory = extras.getIntegerArrayList(EXTRAS_LOCAL_MUTE_HISTORY);
@@ -801,11 +834,7 @@ public class JumbleService extends Service implements IJumbleService, JumbleConn
@Override
public int getTransmitMode() {
- try {
- return getAudioHandler().getTransmitMode();
- } catch (NotSynchronizedException e) {
- throw new IllegalStateException(e);
- }
+ return mTransmitMode;
}
@Override
@@ -846,32 +875,12 @@ public class JumbleService extends Service implements IJumbleService, JumbleConn
@Override
public boolean isTalking() {
- try {
- return getAudioHandler().isRecording();
- } catch (NotSynchronizedException e) {
- throw new IllegalStateException(e);
- }
+ return mToggleInputMode.isTalkingOn();
}
@Override
public void setTalkingState(boolean talking) {
- if (getSessionUser().isSelfMuted() || getSessionUser().isMuted())
- return;
-
- try {
- if (getAudioHandler().getTransmitMode() != Constants.TRANSMIT_PUSH_TO_TALK) {
- Log.w(Constants.TAG, "Attempted to set talking state when not using PTT");
- return;
- }
-
- try {
- getAudioHandler().setTalking(talking);
- } catch (AudioException e) {
- logError(e.getMessage());
- }
- } catch (NotSynchronizedException e) {
- throw new IllegalStateException(e);
- }
+ mToggleInputMode.setTalkingOn(talking);
}
@Override
diff --git a/src/main/java/com/morlunk/jumble/audio/AudioInput.java b/src/main/java/com/morlunk/jumble/audio/AudioInput.java
index ed44938..f21bd32 100644
--- a/src/main/java/com/morlunk/jumble/audio/AudioInput.java
+++ b/src/main/java/com/morlunk/jumble/audio/AudioInput.java
@@ -32,28 +32,18 @@ import com.morlunk.jumble.protocol.AudioHandler;
*/
public class AudioInput implements Runnable {
public static final int[] SAMPLE_RATES = { 48000, 44100, 16000, 8000 };
- private static final int SPEECH_DETECT_THRESHOLD = (int) (0.25 * Math.pow(10, 9)); // Continue speech for 250ms to prevent dropping
// AudioRecord state
private AudioInputListener mListener;
private AudioRecord mAudioRecord;
private final int mFrameSize;
- // Preferences
- private int mTransmitMode;
- private float mVADThreshold;
- private float mAmplitudeBoost = 1.0f;
-
private Thread mRecordThread;
private boolean mRecording;
- public AudioInput(AudioInputListener listener, int audioSource, int targetSampleRate,
- int transmitMode, float vadThreshold, float amplitudeBoost) throws
- NativeAudioException, AudioInitializationException {
+ public AudioInput(AudioInputListener listener, int audioSource, int targetSampleRate)
+ throws NativeAudioException, AudioInitializationException {
mListener = listener;
- mTransmitMode = transmitMode;
- mVADThreshold = vadThreshold;
- mAmplitudeBoost = amplitudeBoost;
// Attempt to construct an AudioRecord with the target sample rate first.
// If it fails, keep producing AudioRecord instances until we find one that initializes
@@ -122,15 +112,6 @@ public class AudioInput implements Runnable {
}
}
- public void setVADThreshold(float threshold) {
- mVADThreshold = threshold;
- }
-
- public void setAmplitudeBoost(float boost) {
- if(boost < 0) throw new IllegalArgumentException("Amplitude boost must not be a negative number!");
- mAmplitudeBoost = boost;
- }
-
/**
* Stops the record loop and waits on it to finish.
* Releases native audio resources.
@@ -178,68 +159,17 @@ public class AudioInput implements Runnable {
Log.i(Constants.TAG, "AudioInput: started");
- boolean vadLastDetected = false;
- long vadLastDetectedTime = 0;
-
mAudioRecord.startRecording();
if(mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED)
return;
- if(mTransmitMode == Constants.TRANSMIT_CONTINUOUS || mTransmitMode == Constants.TRANSMIT_PUSH_TO_TALK)
- mListener.onTalkStateChange(TalkState.TALKING);
-
final short[] mAudioBuffer = new short[mFrameSize];
// We loop when the 'recording' instance var is true instead of checking audio record state because we want to always cleanly shutdown.
while(mRecording) {
int shortsRead = mAudioRecord.read(mAudioBuffer, 0, mFrameSize);
if(shortsRead > 0) {
- // Boost/reduce amplitude based on user preference
- // TODO: perhaps amplify to the largest value that does not result in clipping.
- if(mAmplitudeBoost != 1.0f) {
- for(int i = 0; i < mFrameSize; i++) {
- // Java only guarantees the bounded preservation of sign in a narrowing
- // primitive conversion from float -> int, not float -> int -> short.
- float val = mAudioBuffer[i] * mAmplitudeBoost;
- if (val > Short.MAX_VALUE) {
- val = Short.MAX_VALUE;
- } else if (val < Short.MIN_VALUE) {
- val = Short.MIN_VALUE;
- }
- mAudioBuffer[i] = (short) val;
- }
- }
-
- boolean talking = true;
-
- if(mTransmitMode == Constants.TRANSMIT_VOICE_ACTIVITY) {
- // Use a logarithmic energy-based scale for VAD.
- float sum = 1.0f;
- for (int i = 0; i < mFrameSize; i++) {
- sum += Math.pow(mAudioBuffer[i], 2);
- }
- float micLevel = (float) Math.sqrt(sum / (float)mFrameSize);
- float peakSignal = (float) (20.0f*Math.log10(micLevel / 32768.0f))/96.0f;
- talking = (peakSignal+1) >= mVADThreshold;
-
- /* Record the last time where VAD was detected in order to prevent speech dropping. */
- if(talking) {
- vadLastDetectedTime = System.nanoTime();
- } else {
- talking = (System.nanoTime() - vadLastDetectedTime) < SPEECH_DETECT_THRESHOLD;
- }
-
- // Update the service with the new talking state if we detected voice.
- if(talking ^ vadLastDetected) {
- mListener.onTalkStateChange(talking ? TalkState.TALKING : TalkState.PASSIVE);
- }
-
- vadLastDetected = talking;
- }
-
- if(talking) {
- mListener.onAudioInputReceived(mAudioBuffer, mFrameSize);
- }
+ mListener.onAudioInputReceived(mAudioBuffer, mFrameSize);
} else {
Log.e(Constants.TAG, "Error fetching audio! AudioRecord error " + shortsRead);
}
@@ -247,13 +177,10 @@ public class AudioInput implements Runnable {
mAudioRecord.stop();
- mListener.onTalkStateChange(TalkState.PASSIVE);
-
Log.i(Constants.TAG, "AudioInput: stopped");
}
public interface AudioInputListener {
- public void onTalkStateChange(TalkState state);
- public void onAudioInputReceived(short[] frame, int frameSize);
+ void onAudioInputReceived(short[] frame, int frameSize);
}
}
diff --git a/src/main/java/com/morlunk/jumble/audio/inputmode/ActivityInputMode.java b/src/main/java/com/morlunk/jumble/audio/inputmode/ActivityInputMode.java
index bd96b30..fd7d2a7 100644
--- a/src/main/java/com/morlunk/jumble/audio/inputmode/ActivityInputMode.java
+++ b/src/main/java/com/morlunk/jumble/audio/inputmode/ActivityInputMode.java
@@ -25,7 +25,7 @@ public class ActivityInputMode implements IInputMode {
// Continue speech for 250ms to prevent dropping.
private static final int SPEECH_DELAY = (int) (0.25 * Math.pow(10, 9));
- private final float mVADThreshold;
+ private float mVADThreshold;
private long mVADLastDetected;
public ActivityInputMode(float detectionThreshold) {
@@ -33,7 +33,7 @@ public class ActivityInputMode implements IInputMode {
}
@Override
- public boolean onInputReceived(short[] pcm, int length) {
+ public boolean shouldTransmit(short[] pcm, int length) {
// Use a logarithmic energy-based scale for VAD.
float sum = 1.0f;
for (int i = 0; i < length; i++) {
@@ -43,13 +43,17 @@ public class ActivityInputMode implements IInputMode {
float peakSignal = (float) (20.0f * Math.log10(micLevel / 32768.0f)) / 96.0f;
boolean talking = (peakSignal + 1) >= mVADThreshold;
- talking |= (System.nanoTime() - mVADLastDetected) < SPEECH_DELAY;
-
// Record the last time where VAD was detected in order to prevent speech dropping.
if(talking) {
mVADLastDetected = System.nanoTime();
}
+ talking |= (System.nanoTime() - mVADLastDetected) < SPEECH_DELAY;
+
return talking;
}
+
+ public void setThreshold(float threshold) {
+ mVADThreshold = threshold;
+ }
}
diff --git a/src/main/java/com/morlunk/jumble/audio/inputmode/ContinuousInputMode.java b/src/main/java/com/morlunk/jumble/audio/inputmode/ContinuousInputMode.java
index cd0c282..9405e00 100644
--- a/src/main/java/com/morlunk/jumble/audio/inputmode/ContinuousInputMode.java
+++ b/src/main/java/com/morlunk/jumble/audio/inputmode/ContinuousInputMode.java
@@ -23,7 +23,7 @@ package com.morlunk.jumble.audio.inputmode;
*/
public class ContinuousInputMode implements IInputMode {
@Override
- public boolean onInputReceived(short[] pcm, int length) {
+ public boolean shouldTransmit(short[] pcm, int length) {
return true;
}
}
diff --git a/src/main/java/com/morlunk/jumble/audio/inputmode/IInputMode.java b/src/main/java/com/morlunk/jumble/audio/inputmode/IInputMode.java
index 78f4cf8..4244afc 100644
--- a/src/main/java/com/morlunk/jumble/audio/inputmode/IInputMode.java
+++ b/src/main/java/com/morlunk/jumble/audio/inputmode/IInputMode.java
@@ -28,5 +28,5 @@ public interface IInputMode {
* @param length The number of shorts in the PCM data.
* @return true if the input should be transmitted.
*/
- boolean onInputReceived(short[] pcm, int length);
+ boolean shouldTransmit(short[] pcm, int length);
}
diff --git a/src/main/java/com/morlunk/jumble/audio/inputmode/ToggleInputMode.java b/src/main/java/com/morlunk/jumble/audio/inputmode/ToggleInputMode.java
index 8466805..1a86e62 100644
--- a/src/main/java/com/morlunk/jumble/audio/inputmode/ToggleInputMode.java
+++ b/src/main/java/com/morlunk/jumble/audio/inputmode/ToggleInputMode.java
@@ -41,7 +41,7 @@ public class ToggleInputMode implements IInputMode {
}
@Override
- public boolean onInputReceived(short[] pcm, int length) {
+ public boolean shouldTransmit(short[] pcm, int length) {
return mInputOn;
}
}
diff --git a/src/main/java/com/morlunk/jumble/protocol/AudioHandler.java b/src/main/java/com/morlunk/jumble/protocol/AudioHandler.java
index 1d5273f..3d17fe2 100644
--- a/src/main/java/com/morlunk/jumble/protocol/AudioHandler.java
+++ b/src/main/java/com/morlunk/jumble/protocol/AudioHandler.java
@@ -17,13 +17,9 @@
package com.morlunk.jumble.protocol;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.media.AudioManager;
import android.util.Log;
-import android.widget.Toast;
import com.morlunk.jumble.Constants;
import com.morlunk.jumble.R;
@@ -35,6 +31,7 @@ import com.morlunk.jumble.audio.encoder.IEncoder;
import com.morlunk.jumble.audio.encoder.OpusEncoder;
import com.morlunk.jumble.audio.encoder.PreprocessingEncoder;
import com.morlunk.jumble.audio.encoder.ResamplingEncoder;
+import com.morlunk.jumble.audio.inputmode.IInputMode;
import com.morlunk.jumble.exception.AudioException;
import com.morlunk.jumble.exception.AudioInitializationException;
import com.morlunk.jumble.exception.NativeAudioException;
@@ -50,8 +47,7 @@ import com.morlunk.jumble.util.JumbleNetworkListener;
/**
* Bridges the protocol's audio messages to our input and output threads.
* A useful intermediate for reducing code coupling.
- * Audio playback and recording is exclusively controlled by the protocol. The user can 'hint' to
- * the handler that it wishes to talk with {@link #setTalking(boolean)}.
+ * Audio playback and recording is exclusively controlled by the protocol.
* Changes to input/output instance vars after the audio threads have been initialized will recreate
* them in most cases (they're immutable for the purpose of avoiding threading issues).
* Calling shutdown() will cleanup both input and output threads. It is safe to restart after.
@@ -80,16 +76,10 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
private int mSampleRate;
private int mBitrate;
private int mFramesPerPacket;
- private int mTransmitMode;
- private final float mVADThreshold;
+ private final IInputMode mInputMode;
private final float mAmplitudeBoost;
private boolean mInitialized;
- /**
- * True if the user wants to transmit voice.
- * Always true for voice activity and continuous input methods.
- */
- private boolean mTalking;
/** True if the user is muted on the server. */
private boolean mMuted;
private boolean mBluetoothOn;
@@ -100,7 +90,7 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
public AudioHandler(Context context, JumbleLogger logger, int audioStream, int audioSource,
int sampleRate, int targetBitrate, int targetFramesPerPacket,
- int transmitMode, float vadThreshold, float amplitudeBoost,
+ IInputMode inputMode, float amplitudeBoost,
boolean bluetoothEnabled, boolean halfDuplexEnabled,
boolean preprocessorEnabled, AudioEncodeListener encodeListener,
AudioOutput.AudioOutputListener outputListener) throws AudioInitializationException, NativeAudioException {
@@ -111,8 +101,7 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
mSampleRate = sampleRate;
mBitrate = targetBitrate;
mFramesPerPacket = targetFramesPerPacket;
- mTransmitMode = transmitMode;
- mVADThreshold = vadThreshold;
+ mInputMode = inputMode;
mAmplitudeBoost = amplitudeBoost;
mBluetoothOn = bluetoothEnabled;
mHalfDuplex = halfDuplexEnabled;
@@ -123,13 +112,12 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mEncoderLock = new Object();
- mInput = new AudioInput(this, mAudioSource, mSampleRate, mTransmitMode,
- mVADThreshold, mAmplitudeBoost);
+ mInput = new AudioInput(this, mAudioSource, mSampleRate);
mOutput = new AudioOutput(mOutputListener);
}
/**
- * Starts the audio output thread, and input if {@link #isTalking()} and not muted.
+ * Starts the audio output and input threads.
* Will create both the input and output modules if they haven't been created yet.
*/
public synchronized void initialize(User self, int maxBandwidth, JumbleUDPMessageType codec) throws AudioException {
@@ -138,12 +126,8 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
setMaxBandwidth(maxBandwidth);
setCodec(codec);
-
- setTalking(mTransmitMode == Constants.TRANSMIT_CONTINUOUS
- || mTransmitMode == Constants.TRANSMIT_VOICE_ACTIVITY);
setServerMuted(self.isMuted() || self.isLocalMuted() || self.isSuppressed());
- if (mTalking && !mMuted)
- startRecording();
+ startRecording();
// Ensure that if a bluetooth SCO connection is active, we use the VOICE_CALL stream.
// This is required by Android for compatibility with SCO.
mOutput.startPlaying(mBluetoothOn ? AudioManager.STREAM_VOICE_CALL : mAudioStream);
@@ -160,7 +144,7 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
synchronized (mInput) {
if (!mInput.isRecording()) {
mInput.startRecording();
- if (mHalfDuplex && mTransmitMode == Constants.TRANSMIT_PUSH_TO_TALK) {
+ if (mHalfDuplex) {
mAudioManager.setStreamMute(getAudioStream(), true);
}
} else {
@@ -177,7 +161,7 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
synchronized (mInput) {
if (mInput.isRecording()) {
mInput.stopRecording();
- if (mHalfDuplex && mTransmitMode == Constants.TRANSMIT_PUSH_TO_TALK) {
+ if (mHalfDuplex) {
mAudioManager.setStreamMute(getAudioStream(), false);
}
} else {
@@ -187,44 +171,11 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
}
/**
- * Sets whether we should record if the user is not muted or deafened.
- * This defaults to true when using voice activity or continous input, false for push-to-talk.
- * @param talking Whether to record, if available.
- */
- public synchronized void setTalking(boolean talking) throws AudioException {
- mTalking = talking;
- // We start recording on initialization.
- if (mInitialized) {
- if (!mMuted && talking && !isRecording())
- startRecording();
- else if (!talking && isRecording())
- stopRecording();
- }
- }
-
- /**
- * Returns the talking state of the client.
- * This does **NOT** mean we are recording!
- * @return true if the client wants to be talking.
- */
- public boolean isTalking() {
- return mTalking;
- }
-
- /**
* Sets whether or not the server wants the client muted.
- * If the user is muted by the server, audio input will be suspended.
- * If the user is unmuted by the server, audio input will be unsuspended if {@link #isTalking()}.
* @param muted Whether the user is muted on the server.
*/
private void setServerMuted(boolean muted) throws AudioException {
mMuted = muted;
- if (mInitialized) {
- if (!muted && mTalking && !isRecording())
- startRecording();
- else if (muted && isRecording())
- stopRecording();
- }
}
/**
@@ -235,16 +186,6 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
return mInitialized;
}
- /**
- * User is recording if isTalking() && !isMuted().
- * @return
- */
- public boolean isRecording() {
- synchronized (mInput) {
- return mInput.isRecording();
- }
- }
-
public boolean isPlaying() {
synchronized (mOutput) {
return mOutput.isPlaying();
@@ -353,14 +294,6 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
return mFramesPerPacket;
}
- public int getTransmitMode() {
- return mTransmitMode;
- }
-
- public float getVADThreshold() {
- return mVADThreshold;
- }
-
public float getAmplitudeBoost() {
return mAmplitudeBoost;
}
@@ -457,36 +390,49 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
}
@Override
- public void onTalkStateChange(TalkState state) {
+ public void onAudioInputReceived(short[] frame, int frameSize) {
+ boolean talking = mInputMode.shouldTransmit(frame, frameSize);
+ mEncodeListener.setTransmitting(talking);
synchronized (mEncoderLock) {
- if (mEncoder != null && state == TalkState.PASSIVE) {
- try {
- mEncoder.terminate();
- if (mEncoder.isReady()) {
- sendEncodedAudio();
+ if (!talking || mMuted) {
+ if (mEncoder != null) {
+ try {
+ mEncoder.terminate();
+ if (mEncoder.isReady()) {
+ sendEncodedAudio();
+ }
+ } catch (NativeAudioException e) {
+ e.printStackTrace();
}
- } catch (NativeAudioException e) {
- e.printStackTrace();
}
- }
- }
- mEncodeListener.onTalkStateChange(state);
- }
-
- @Override
- public void onAudioInputReceived(short[] frame, int frameSize) {
- synchronized (mEncoderLock) {
- if (mEncoder != null) {
- try {
- mEncoder.encode(frame, frameSize);
- mFrameCounter++;
- } catch (NativeAudioException e) {
- e.printStackTrace();
- return;
+ } else {
+ // Boost/reduce amplitude based on user preference
+ // TODO: perhaps amplify to the largest value that does not result in clipping.
+ if (mAmplitudeBoost != 1.0f) {
+ for (int i = 0; i < frameSize; i++) {
+ // Java only guarantees the bounded preservation of sign in a narrowing
+ // primitive conversion from float -> int, not float -> int -> short.
+ float val = frame[i] * mAmplitudeBoost;
+ if (val > Short.MAX_VALUE) {
+ val = Short.MAX_VALUE;
+ } else if (val < Short.MIN_VALUE) {
+ val = Short.MIN_VALUE;
+ }
+ frame[i] = (short) val;
+ }
}
- if (mEncoder.isReady()) {
- sendEncodedAudio();
+ if (mEncoder != null) {
+ try {
+ mEncoder.encode(frame, frameSize);
+ mFrameCounter++;
+
+ if (mEncoder.isReady()) {
+ sendEncodedAudio();
+ }
+ } catch (NativeAudioException e) {
+ e.printStackTrace();
+ }
}
}
}
@@ -516,8 +462,8 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
}
public interface AudioEncodeListener {
- public void onAudioEncoded(byte[] data, int length);
- public void onTalkStateChange(TalkState state);
+ void onAudioEncoded(byte[] data, int length);
+ void setTransmitting(boolean talking);
}
/**
@@ -531,12 +477,12 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
private int mTargetBitrate;
private int mTargetFramesPerPacket;
private int mInputSampleRate;
- private int mTransmitMode;
private float mVADThreshold;
private float mAmplitudeBoost;
private boolean mBluetoothEnabled;
private boolean mHalfDuplexEnabled;
private boolean mPreprocessorEnabled;
+ private IInputMode mInputMode;
private AudioEncodeListener mEncodeListener;
private AudioOutput.AudioOutputListener mTalkingListener;
@@ -575,11 +521,6 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
return this;
}
- public Builder setTransmitMode(int transmitMode) {
- mTransmitMode = transmitMode;
- return this;
- }
-
public Builder setVADThreshold(float vadThreshold) {
mVADThreshold = vadThreshold;
return this;
@@ -615,14 +556,19 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
return this;
}
+ public Builder setInputMode(IInputMode inputMode) {
+ mInputMode = inputMode;
+ return this;
+ }
+
/**
* Creates a new AudioHandler for the given session and begins managing input/output.
* @return An initialized audio handler.
*/
public AudioHandler initialize(User self, int maxBandwidth, JumbleUDPMessageType codec) throws AudioException {
AudioHandler handler = new AudioHandler(mContext, mLogger, mAudioStream, mAudioSource,
- mInputSampleRate, mTargetBitrate, mTargetFramesPerPacket, mTransmitMode,
- mVADThreshold, mAmplitudeBoost, mBluetoothEnabled, mHalfDuplexEnabled,
+ mInputSampleRate, mTargetBitrate, mTargetFramesPerPacket, mInputMode,
+ mAmplitudeBoost, mBluetoothEnabled, mHalfDuplexEnabled,
mPreprocessorEnabled, mEncodeListener, mTalkingListener);
handler.initialize(self, maxBandwidth, codec);
return handler;