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@morlunk.com>2015-03-31 05:37:23 +0300
committerAndrew Comminos <andrew@morlunk.com>2015-03-31 05:37:23 +0300
commitcec50689fbf9ceef17aa0cbe57603f34ef942328 (patch)
treea2ecef4e8e1b6ac47290b83a4cdbf5cc164de716
parentf9dcee10f5687f05d2cc0ec30e8fe49a52b39d42 (diff)
Initial progress on a single point for service reconfiguration.
-rw-r--r--src/main/aidl/com/morlunk/jumble/IJumbleService.aidl5
-rw-r--r--src/main/java/com/morlunk/jumble/JumbleService.java230
-rw-r--r--src/main/java/com/morlunk/jumble/protocol/AudioHandler.java245
3 files changed, 263 insertions, 217 deletions
diff --git a/src/main/aidl/com/morlunk/jumble/IJumbleService.aidl b/src/main/aidl/com/morlunk/jumble/IJumbleService.aidl
index fe10252..4451752 100644
--- a/src/main/aidl/com/morlunk/jumble/IJumbleService.aidl
+++ b/src/main/aidl/com/morlunk/jumble/IJumbleService.aidl
@@ -63,15 +63,10 @@ interface IJumbleService {
boolean isTalking();
void setTalkingState(boolean talking);
int getTransmitMode();
- void setTransmitMode(int transmitMode);
- void setVADThreshold(float threshold);
- void setAmplitudeBoost(float boost);
- void setHalfDuplex(boolean enabled);
int getCodec();
// Bluetooth
boolean isBluetoothAvailable();
- void setBluetoothEnabled(boolean enabled);
// Server actions
void joinChannel(int channel);
diff --git a/src/main/java/com/morlunk/jumble/JumbleService.java b/src/main/java/com/morlunk/jumble/JumbleService.java
index 407ad41..ddeeb8e 100644
--- a/src/main/java/com/morlunk/jumble/JumbleService.java
+++ b/src/main/java/com/morlunk/jumble/JumbleService.java
@@ -34,7 +34,6 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.util.Log;
-import com.morlunk.jumble.audio.AudioInput;
import com.morlunk.jumble.audio.AudioOutput;
import com.morlunk.jumble.exception.AudioException;
import com.morlunk.jumble.model.Channel;
@@ -129,26 +128,17 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
private int mAutoReconnectDelay;
private byte[] mCertificate;
private String mCertificatePassword;
- private float mDetectionThreshold;
- private float mAmplitudeBoost;
- private int mTransmitMode;
private boolean mUseOpus;
- private int mInputRate;
- private int mInputQuality;
private boolean mForceTcp;
private boolean mUseTor;
private String mClientName;
private List<String> mAccessTokens;
- private int mAudioSource;
- private int mAudioStream;
- private int mFramesPerPacket;
private String mTrustStore;
private String mTrustStorePassword;
private String mTrustStoreFormat;
- private boolean mHalfDuplex;
private List<Integer> mLocalMuteHistory;
private List<Integer> mLocalIgnoreHistory;
- private boolean mEnablePreprocessor;
+ private AudioHandler.Builder mAudioBuilder;
private PowerManager.WakeLock mWakeLock;
private Handler mHandler;
@@ -232,31 +222,11 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
if (intent != null) {
if (intent.getExtras() != null) {
Bundle extras = intent.getExtras();
- mServer = extras.getParcelable(EXTRAS_SERVER);
- mAutoReconnect = extras.getBoolean(EXTRAS_AUTO_RECONNECT, true);
- mAutoReconnectDelay = extras.getInt(EXTRAS_AUTO_RECONNECT_DELAY, 5000);
- mCertificate = extras.getByteArray(EXTRAS_CERTIFICATE);
- mCertificatePassword = extras.getString(EXTRAS_CERTIFICATE_PASSWORD);
- mDetectionThreshold = extras.getFloat(EXTRAS_DETECTION_THRESHOLD, 0.5f);
- mAmplitudeBoost = extras.getFloat(EXTRAS_AMPLITUDE_BOOST, 1.0f);
- mTransmitMode = extras.getInt(EXTRAS_TRANSMIT_MODE, Constants.TRANSMIT_VOICE_ACTIVITY);
- mInputRate = extras.getInt(EXTRAS_INPUT_RATE, 44100);
- mInputQuality = extras.getInt(EXTRAS_INPUT_QUALITY, 40000);
- mUseOpus = extras.getBoolean(EXTRAS_USE_OPUS, true);
- mUseTor = extras.getBoolean(EXTRAS_USE_TOR, false);
- mForceTcp = extras.getBoolean(EXTRAS_FORCE_TCP, false) || mUseTor; // Tor requires TCP connections to work- if it's on, force TCP.
- mClientName = extras.containsKey(EXTRAS_CLIENT_NAME) ? extras.getString(EXTRAS_CLIENT_NAME) : "Jumble";
- mAccessTokens = extras.getStringArrayList(EXTRAS_ACCESS_TOKENS);
- mAudioSource = extras.getInt(EXTRAS_AUDIO_SOURCE, MediaRecorder.AudioSource.MIC);
- mAudioStream = extras.getInt(EXTRAS_AUDIO_STREAM, AudioManager.STREAM_MUSIC);
- mFramesPerPacket = extras.getInt(EXTRAS_FRAMES_PER_PACKET, 2);
- mTrustStore = extras.getString(EXTRAS_TRUST_STORE);
- mTrustStorePassword = extras.getString(EXTRAS_TRUST_STORE_PASSWORD);
- mTrustStoreFormat = extras.getString(EXTRAS_TRUST_STORE_FORMAT);
- mHalfDuplex = extras.getBoolean(EXTRAS_HALF_DUPLEX);
- mLocalMuteHistory = extras.getIntegerArrayList(EXTRAS_LOCAL_MUTE_HISTORY);
- mLocalIgnoreHistory = extras.getIntegerArrayList(EXTRAS_LOCAL_IGNORE_HISTORY);
- mEnablePreprocessor = extras.getBoolean(EXTRAS_ENABLE_PREPROCESSOR, true);
+ try {
+ loadSettings(extras);
+ } catch (AudioException e) {
+ throw new RuntimeException("Attempted to initialize audio in onStartCommand erroneously.");
+ }
}
if (ACTION_CONNECT.equals(intent.getAction())) {
@@ -280,6 +250,11 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
mHandler = new Handler(getMainLooper());
mCallbacks = new JumbleCallbacks();
mMessageLog = new ArrayList<Message>();
+ mAudioBuilder = new AudioHandler.Builder()
+ .setContext(this)
+ .setLogger(this)
+ .setEncodeListener(mAudioInputListener)
+ .setTalkingListener(mAudioOutputListener);
mConnectionState = STATE_DISCONNECTED;
}
@@ -310,19 +285,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
mModelHandler = new ModelHandler(this, mCallbacks, this,
mLocalMuteHistory, mLocalIgnoreHistory);
- mAudioHandler = new AudioHandler(this, this, mAudioInputListener, mAudioOutputListener);
- mAudioHandler.setAmplitudeBoost(mAmplitudeBoost);
- mAudioHandler.setBitrate(mInputQuality);
- mAudioHandler.setVADThreshold(mDetectionThreshold);
- mAudioHandler.setTransmitMode(mTransmitMode);
- mAudioHandler.setAudioSource(mAudioSource);
- mAudioHandler.setAudioStream(mAudioStream);
- mAudioHandler.setFramesPerPacket(mFramesPerPacket);
- mAudioHandler.setSampleRate(mInputRate);
- mAudioHandler.setHalfDuplex(mHalfDuplex);
- mAudioHandler.setPreprocessorEnabled(mEnablePreprocessor);
- mConnection.addTCPMessageHandlers(mModelHandler, mAudioHandler);
- mConnection.addUDPMessageHandlers(mAudioHandler);
+ mConnection.addTCPMessageHandlers(mModelHandler);
mConnectionState = STATE_CONNECTING;
@@ -340,14 +303,6 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
} catch (RemoteException e1) {
e1.printStackTrace();
}
- } catch (AudioException e) {
- e.printStackTrace();
- try {
- mCallbacks.onDisconnected(new JumbleException(e,
- JumbleException.JumbleDisconnectReason.OTHER_ERROR));
- } catch (RemoteException e1) {
- e1.printStackTrace();
- }
}
}
@@ -389,9 +344,11 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
mWakeLock.acquire();
try {
- mAudioHandler.initialize(
+ mAudioHandler = mAudioBuilder.initialize(
mModelHandler.getUser(mConnection.getSession()),
- mConnection.getCodec());
+ mConnection.getMaxBandwidth(), mConnection.getCodec());
+ mConnection.addTCPMessageHandlers(mModelHandler, mAudioHandler);
+ mConnection.addUDPMessageHandlers(mAudioHandler);
} catch (AudioException e) {
e.printStackTrace();
onConnectionWarning(e.getMessage());
@@ -509,6 +466,116 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
}
}
+ /**
+ * Loads all defined settings from the given bundle into the JumbleService.
+ * Some settings may only take effect after a reconnect.
+ * @param extras A bundle with settings.
+ * @return true if a reconnect is required for changes to take effect.
+ * @see com.morlunk.jumble.JumbleService
+ */
+ private boolean loadSettings(Bundle extras) throws AudioException {
+ boolean reconnectNeeded = false;
+ if (extras.containsKey(EXTRAS_SERVER)) {
+ mServer = extras.getParcelable(EXTRAS_SERVER);
+ reconnectNeeded = true;
+ }
+ if (extras.containsKey(EXTRAS_AUTO_RECONNECT)) {
+ mAutoReconnect = extras.getBoolean(EXTRAS_AUTO_RECONNECT);
+ }
+ if (extras.containsKey(EXTRAS_AUTO_RECONNECT_DELAY)) {
+ mAutoReconnectDelay = extras.getInt(EXTRAS_AUTO_RECONNECT_DELAY);
+ }
+ if (extras.containsKey(EXTRAS_CERTIFICATE)) {
+ mCertificate = extras.getByteArray(EXTRAS_CERTIFICATE);
+ reconnectNeeded = true;
+ }
+ if (extras.containsKey(EXTRAS_CERTIFICATE_PASSWORD)) {
+ mCertificatePassword = extras.getString(EXTRAS_CERTIFICATE_PASSWORD);
+ reconnectNeeded = true;
+ }
+ if (extras.containsKey(EXTRAS_DETECTION_THRESHOLD)) {
+ mAudioBuilder.setVADThreshold(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));
+ }
+ if (extras.containsKey(EXTRAS_INPUT_RATE)) {
+ mAudioBuilder.setInputSampleRate(extras.getInt(EXTRAS_INPUT_RATE));
+ }
+ if (extras.containsKey(EXTRAS_INPUT_QUALITY)) {
+ mAudioBuilder.setTargetBitrate(extras.getInt(EXTRAS_INPUT_QUALITY));
+ }
+ if (extras.containsKey(EXTRAS_USE_OPUS)) {
+ mUseOpus = extras.getBoolean(EXTRAS_USE_OPUS);
+ reconnectNeeded = true;
+ }
+ if (extras.containsKey(EXTRAS_USE_TOR)) {
+ mUseTor = extras.getBoolean(EXTRAS_USE_TOR);
+ mForceTcp |= mUseTor; // Tor requires TCP connections to work- if it's on, force TCP.
+ reconnectNeeded = true;
+ }
+ if (extras.containsKey(EXTRAS_FORCE_TCP)) {
+ mForceTcp |= extras.getBoolean(EXTRAS_FORCE_TCP);
+ reconnectNeeded = true;
+ }
+ if (extras.containsKey(EXTRAS_CLIENT_NAME)) {
+ mClientName = extras.getString(EXTRAS_CLIENT_NAME);
+ reconnectNeeded = true;
+ }
+ if (extras.containsKey(EXTRAS_ACCESS_TOKENS)) {
+ mAccessTokens = extras.getStringArrayList(EXTRAS_ACCESS_TOKENS);
+ // TODO: send access tokens
+ }
+ if (extras.containsKey(EXTRAS_AUDIO_SOURCE)) {
+ mAudioBuilder.setAudioSource(extras.getInt(EXTRAS_AUDIO_SOURCE));
+ }
+ if (extras.containsKey(EXTRAS_AUDIO_STREAM)) {
+ mAudioBuilder.setAudioStream(extras.getInt(EXTRAS_AUDIO_STREAM));
+ }
+ if (extras.containsKey(EXTRAS_FRAMES_PER_PACKET)) {
+ mAudioBuilder.setTargetFramesPerPacket(extras.getInt(EXTRAS_FRAMES_PER_PACKET));
+ }
+ if (extras.containsKey(EXTRAS_TRUST_STORE)) {
+ mTrustStore = extras.getString(EXTRAS_TRUST_STORE);
+ reconnectNeeded = true;
+ }
+ if (extras.containsKey(EXTRAS_TRUST_STORE_PASSWORD)) {
+ mTrustStorePassword = extras.getString(EXTRAS_TRUST_STORE_PASSWORD);
+ reconnectNeeded = true;
+ }
+ if (extras.containsKey(EXTRAS_TRUST_STORE_FORMAT)) {
+ mTrustStoreFormat = extras.getString(EXTRAS_TRUST_STORE_FORMAT);
+ reconnectNeeded = true;
+ }
+ if (extras.containsKey(EXTRAS_HALF_DUPLEX)) {
+ mAudioBuilder.setHalfDuplexEnabled(extras.getBoolean(EXTRAS_HALF_DUPLEX));
+ }
+ if (extras.containsKey(EXTRAS_LOCAL_MUTE_HISTORY)) {
+ mLocalMuteHistory = extras.getIntegerArrayList(EXTRAS_LOCAL_MUTE_HISTORY);
+ reconnectNeeded = true;
+ }
+ if (extras.containsKey(EXTRAS_LOCAL_IGNORE_HISTORY)) {
+ mLocalIgnoreHistory = extras.getIntegerArrayList(EXTRAS_LOCAL_IGNORE_HISTORY);
+ reconnectNeeded = true;
+ }
+ if (extras.containsKey(EXTRAS_ENABLE_PREPROCESSOR)) {
+ mAudioBuilder.setPreprocessorEnabled(extras.getBoolean(EXTRAS_ENABLE_PREPROCESSOR));
+ }
+
+ // Reload audio subsystem if initialized
+ if (mAudioHandler != null && mAudioHandler.isInitialized()) {
+ mAudioHandler.shutdown();
+ mAudioHandler = mAudioBuilder.initialize(
+ mModelHandler.getUser(mConnection.getSession()),
+ mConnection.getMaxBandwidth(), mConnection.getCodec());
+ Log.i(Constants.TAG, "Audio subsystem reloaded after settings change.");
+ }
+ return reconnectNeeded;
+ }
+
public class JumbleBinder extends IJumbleService.Stub {
@Override
public int getConnectionState() throws RemoteException {
@@ -635,34 +702,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
@Override
public int getTransmitMode() throws RemoteException {
- return mTransmitMode;
- }
-
- @Override
- public void setTransmitMode(int transmitMode) throws RemoteException {
- mTransmitMode = transmitMode;
- try {
- mAudioHandler.setTransmitMode(transmitMode);
- } catch (AudioException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public void setVADThreshold(float threshold) throws RemoteException {
- mDetectionThreshold = threshold;
- mAudioHandler.setVADThreshold(threshold);
- }
-
- @Override
- public void setAmplitudeBoost(float boost) throws RemoteException {
- mAmplitudeBoost = boost;
- mAudioHandler.setAmplitudeBoost(boost);
- }
-
- @Override
- public void setHalfDuplex(boolean enabled) throws RemoteException {
- mAudioHandler.setHalfDuplex(enabled);
+ return mAudioHandler.getTransmitMode();
}
@Override
@@ -682,7 +722,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
return;
}
- if (mTransmitMode != Constants.TRANSMIT_PUSH_TO_TALK) {
+ if (mAudioHandler.getTransmitMode() != Constants.TRANSMIT_PUSH_TO_TALK) {
Log.w(Constants.TAG, "Attempted to set talking state when not using PTT");
return;
}
@@ -701,20 +741,6 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
}
@Override
- public void setBluetoothEnabled(boolean enabled) throws RemoteException {
- AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
- if(enabled) {
- try {
- audioManager.startBluetoothSco();
- } catch (NullPointerException e) {
- // Workaround for NPE thrown here on Lollipop when no devices are connected.
- }
- } else {
- audioManager.stopBluetoothSco();
- }
- }
-
- @Override
public void joinChannel(int channel) throws RemoteException {
moveUserToChannel(getSession(), channel);
}
diff --git a/src/main/java/com/morlunk/jumble/protocol/AudioHandler.java b/src/main/java/com/morlunk/jumble/protocol/AudioHandler.java
index 0fc347c..32b8d08 100644
--- a/src/main/java/com/morlunk/jumble/protocol/AudioHandler.java
+++ b/src/main/java/com/morlunk/jumble/protocol/AudioHandler.java
@@ -75,14 +75,14 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
private IEncoder mEncoder;
private int mFrameCounter;
- private int mAudioStream;
- private int mAudioSource;
+ private final int mAudioStream;
+ private final int mAudioSource;
private int mSampleRate;
private int mBitrate;
private int mFramesPerPacket;
private int mTransmitMode;
- private float mVADThreshold;
- private float mAmplitudeBoost = 1.0f;
+ private final float mVADThreshold;
+ private final float mAmplitudeBoost;
private boolean mInitialized;
/**
@@ -131,12 +131,28 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
}
};
- public AudioHandler(Context context, JumbleLogger logger, AudioEncodeListener encodeListener,
+ public AudioHandler(Context context, JumbleLogger logger, int audioStream, int audioSource,
+ int sampleRate, int targetBitrate, int targetFramesPerPacket,
+ int transmitMode, float vadThreshold, float amplitudeBoost,
+ boolean bluetoothEnabled, boolean halfDuplexEnabled,
+ boolean preprocessorEnabled, AudioEncodeListener encodeListener,
AudioOutput.AudioOutputListener outputListener) {
mContext = context;
mLogger = logger;
+ mAudioStream = audioStream;
+ mAudioSource = audioSource;
+ mSampleRate = sampleRate;
+ mBitrate = targetBitrate;
+ mFramesPerPacket = targetFramesPerPacket;
+ mTransmitMode = transmitMode;
+ mVADThreshold = vadThreshold;
+ mAmplitudeBoost = amplitudeBoost;
+ mBluetoothOn = bluetoothEnabled;
+ mHalfDuplex = halfDuplexEnabled;
+ mPreprocessorEnabled = preprocessorEnabled;
mEncodeListener = encodeListener;
mOutputListener = outputListener;
+
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mEncoderLock = new Object();
}
@@ -169,7 +185,7 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
* Starts the audio output thread, and input if {@link #isTalking()} and not muted.
* Will create both the input and output modules if they haven't been created yet.
*/
- public synchronized void initialize(User self, JumbleUDPMessageType codec) throws AudioException {
+ public synchronized void initialize(User self, int maxBandwidth, JumbleUDPMessageType codec) throws AudioException {
if(mInitialized) return;
mSession = self.getSession();
setServerMuted(self.isMuted() || self.isLocalMuted() || self.isSuppressed());
@@ -178,6 +194,7 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
// This sticky broadcast will initialize the audio output.
mContext.registerReceiver(mBluetoothReceiver, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
mInitialized = true;
+ setMaxBandwidth(maxBandwidth);
setCodec(codec);
}
@@ -326,69 +343,24 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
return mAudioStream;
}
- /**
- * Sets the output stream for audio, such as {@link android.media.AudioManager#STREAM_VOICE_CALL}.
- * The output thread will be automatically recreated if currently playing.
- * @param audioStream A constant representing an Android stream. Found in {@link android.media.AudioManager}.
- */
- public void setAudioStream(int audioStream) {
- this.mAudioStream = audioStream;
- if(mInitialized) createAudioOutput();
- }
-
public int getAudioSource() {
return mAudioSource;
}
- /**
- * Sets the input source for audio, i.e. back microphone, front microphone.
- * The input thread will be automatically respawned if currently recording.
- * @param audioSource A constant representing an Android audio source. Found in {@link android.media.MediaRecorder.AudioSource}.
- */
- public void setAudioSource(int audioSource) throws AudioException {
- this.mAudioSource = audioSource;
- if(mInitialized) createAudioInput();
- }
-
public int getSampleRate() {
return mSampleRate;
}
- /**
- * Attempts to set the sample rate of the audio input thread.
- * If the desired sample rate is not supported, the next best one will be chosen.
- * The input thread will be automatically respawned if currently recording.
- * @param sampleRate The desired sample rate.
- */
- public void setSampleRate(int sampleRate) throws AudioException {
- this.mSampleRate = sampleRate;
- if(mInitialized) createAudioInput();
- }
-
public int getBitrate() {
return mBitrate;
}
/**
- * Sets the bitrate of the encoder.
- * Triggers an encoder recreation if initialized.
- * @param bitrate The desired bitrate.
- */
- public void setBitrate(int bitrate) throws NativeAudioException {
- this.mBitrate = bitrate;
- synchronized (mEncoderLock) {
- if (mCodec != null && mEncoder != null) {
- recreateEncoder();
- }
- }
- }
-
- /**
* Sets the maximum bandwidth available for audio input as obtained from the server.
* Adjusts the bitrate and frames per packet accordingly to meet the server's requirement.
* @param maxBandwidth The server-reported maximum bandwidth, in bps.
*/
- public void setMaxBandwidth(int maxBandwidth) throws AudioException {
+ private void setMaxBandwidth(int maxBandwidth) throws AudioException {
if (maxBandwidth == -1) {
return;
}
@@ -412,8 +384,8 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
if (bitrate != mBitrate ||
framesPerPacket != mFramesPerPacket) {
- setBitrate(bitrate);
- setFramesPerPacket(framesPerPacket);
+ mBitrate = bitrate;
+ mFramesPerPacket = framesPerPacket;
mLogger.log(Message.Type.INFO, mContext.getString(R.string.audio_max_bandwidth,
maxBandwidth/1000, maxBandwidth/1000, framesPerPacket * 10));
@@ -424,20 +396,6 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
return mFramesPerPacket;
}
- /**
- * Sets the number of frames per packet to be encoded before sending to the server.
- * Recreates encoder, if created.
- * @param framesPerPacket The number of frames per audio packet.
- */
- public void setFramesPerPacket(int framesPerPacket) throws AudioException {
- this.mFramesPerPacket = framesPerPacket;
- synchronized (mEncoderLock) {
- if (mCodec != null && mEncoder != null) {
- recreateEncoder();
- }
- }
- }
-
public int getTransmitMode() {
return mTransmitMode;
}
@@ -458,31 +416,11 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
return mVADThreshold;
}
- /**
- * Sets the threshold for voice activation transmission.
- * Does not require input thread recreation.
- * @param threshold An arbitrary floating point value to use in determining speech.
- */
- public void setVADThreshold(float threshold) {
- this.mVADThreshold = threshold;
- if(mInitialized) mInput.setVADThreshold(threshold);
- }
-
public float getAmplitudeBoost() {
return mAmplitudeBoost;
}
/**
- * Sets an amplitude boost multiplier for audio input.
- * Does not require input thread recreation.
- * @param boost An floating point value to multiply raw PCM data by in the range [0, {@link Float#MAX_VALUE}].
- */
- public void setAmplitudeBoost(float boost) {
- this.mAmplitudeBoost = boost;
- if(mInitialized) mInput.setAmplitudeBoost(boost);
- }
-
- /**
* Returns whether or not the audio handler is operating in half duplex mode, muting outgoing
* audio when incoming audio is received.
* @return true if the handler is in half duplex mode.
@@ -491,34 +429,12 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
return mHalfDuplex;
}
- /**
- * Sets whether or not the audio handler should operate in half duplex mode, muting outgoing
- * audio when incoming audio is received.
- * Does not require input thread recreation.
- * @param halfDuplex Whether to enable half duplex mode.
- */
- public void setHalfDuplex(boolean halfDuplex) {
- mHalfDuplex = halfDuplex;
- }
-
- /**
- * Sets whether to enable the Speex preprocessor.
- * Does not require input thread recreation.
- * @param preprocessorEnabled Whether to enable the Speex preprocessor.
- */
- public void setPreprocessorEnabled(boolean preprocessorEnabled) {
- mPreprocessorEnabled = preprocessorEnabled;
- // FIXME
- }
-
public int getCurrentBandwidth() {
return JumbleConnection.calculateAudioBandwidth(mBitrate, mFramesPerPacket);
}
/**
* Shuts down the audio handler, halting input and output.
- * The handler may still be reinitialized with
- * {@link AudioHandler#initialize(int, com.morlunk.jumble.net.JumbleUDPMessageType)} after.
*/
public synchronized void shutdown() {
if(mInput != null) {
@@ -666,4 +582,113 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
public void onAudioEncoded(byte[] data, int length);
public void onTalkStateChange(User.TalkState state);
}
+
+ /**
+ * A builder to configure and instantiate the audio protocol handler.
+ */
+ public static class Builder {
+ private Context mContext;
+ private JumbleLogger mLogger;
+ private int mAudioStream;
+ private int mAudioSource;
+ 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 AudioEncodeListener mEncodeListener;
+ private AudioOutput.AudioOutputListener mTalkingListener;
+
+ public Builder setContext(Context context) {
+ mContext = context;
+ return this;
+ }
+
+ public Builder setLogger(JumbleLogger logger) {
+ mLogger = logger;
+ return this;
+ }
+
+ public Builder setAudioStream(int audioStream) {
+ mAudioStream = audioStream;
+ return this;
+ }
+
+ public Builder setAudioSource(int audioSource) {
+ mAudioSource = audioSource;
+ return this;
+ }
+
+ public Builder setTargetBitrate(int targetBitrate) {
+ mTargetBitrate = targetBitrate;
+ return this;
+ }
+
+ public Builder setTargetFramesPerPacket(int targetFramesPerPacket) {
+ mTargetFramesPerPacket = targetFramesPerPacket;
+ return this;
+ }
+
+ public Builder setInputSampleRate(int inputSampleRate) {
+ mInputSampleRate = inputSampleRate;
+ return this;
+ }
+
+ public Builder setTransmitMode(int transmitMode) {
+ mTransmitMode = transmitMode;
+ return this;
+ }
+
+ public Builder setVADThreshold(float vadThreshold) {
+ mVADThreshold = vadThreshold;
+ return this;
+ }
+
+ public Builder setAmplitudeBoost(float amplitudeBoost) {
+ mAmplitudeBoost = amplitudeBoost;
+ return this;
+ }
+
+ public Builder setBluetoothEnabled(boolean bluetoothEnabled) {
+ mBluetoothEnabled = bluetoothEnabled;
+ return this;
+ }
+
+ public Builder setHalfDuplexEnabled(boolean halfDuplexEnabled) {
+ mHalfDuplexEnabled = halfDuplexEnabled;
+ return this;
+ }
+
+ public Builder setPreprocessorEnabled(boolean preprocessorEnabled) {
+ mPreprocessorEnabled = preprocessorEnabled;
+ return this;
+ }
+
+ public Builder setEncodeListener(AudioEncodeListener encodeListener) {
+ mEncodeListener = encodeListener;
+ return this;
+ }
+
+ public Builder setTalkingListener(AudioOutput.AudioOutputListener talkingListener) {
+ mTalkingListener = talkingListener; // TODO: remove user dependency from AudioOutput
+ 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,
+ mPreprocessorEnabled, mEncodeListener, mTalkingListener);
+ handler.initialize(self, maxBandwidth, codec);
+ return handler;
+ }
+ }
}