diff options
author | Andrew Comminos <andrew@morlunk.com> | 2015-03-31 05:37:23 +0300 |
---|---|---|
committer | Andrew Comminos <andrew@morlunk.com> | 2015-03-31 05:37:23 +0300 |
commit | cec50689fbf9ceef17aa0cbe57603f34ef942328 (patch) | |
tree | a2ecef4e8e1b6ac47290b83a4cdbf5cc164de716 | |
parent | f9dcee10f5687f05d2cc0ec30e8fe49a52b39d42 (diff) |
Initial progress on a single point for service reconfiguration.
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; + } + } } |