diff options
author | Andrew Comminos <andrewcomminos@gmail.com> | 2013-12-07 07:08:31 +0400 |
---|---|---|
committer | Andrew Comminos <andrewcomminos@gmail.com> | 2013-12-07 07:08:31 +0400 |
commit | eae58cca15df72ee7b3cc2d0a0311b38ed739f23 (patch) | |
tree | a037be9b0834fd54e1ead71ce34eab28a43524e7 /src/main/java | |
parent | 2d851e3951caada8a6e5375760fd9c7849777eaf (diff) |
Added bluetooth support, audio input and output resource management.
Diffstat (limited to 'src/main/java')
-rw-r--r-- | src/main/java/com/morlunk/jumble/JumbleService.java | 73 | ||||
-rw-r--r-- | src/main/java/com/morlunk/jumble/audio/AudioInput.java | 16 | ||||
-rw-r--r-- | src/main/java/com/morlunk/jumble/audio/AudioOutput.java | 36 |
3 files changed, 108 insertions, 17 deletions
diff --git a/src/main/java/com/morlunk/jumble/JumbleService.java b/src/main/java/com/morlunk/jumble/JumbleService.java index d73d71d..279fe61 100644 --- a/src/main/java/com/morlunk/jumble/JumbleService.java +++ b/src/main/java/com/morlunk/jumble/JumbleService.java @@ -17,14 +17,19 @@ package com.morlunk.jumble; import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; +import android.media.AudioManager; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Log; +import android.widget.Toast; import com.morlunk.jumble.audio.AudioInput; import com.morlunk.jumble.audio.AudioOutput; @@ -94,6 +99,43 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon private AudioOutput mAudioOutput; private AudioInput mAudioInput; + private AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() { + @Override + public void onAudioFocusChange(int focusChange) { + switch (focusChange) { + case AudioManager.AUDIOFOCUS_GAIN: + mAudioOutput.startPlaying(false); + break; + case AudioManager.AUDIOFOCUS_LOSS: + mAudioOutput.stopPlaying(); + break; + } + } + }; + + private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int audioState = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_ERROR); + switch (audioState) { + case AudioManager.SCO_AUDIO_STATE_CONNECTED: + Toast.makeText(JumbleService.this, R.string.bluetooth_connected, Toast.LENGTH_LONG).show(); + mAudioOutput.stopPlaying(); + if(isConnected()) + mAudioOutput.startPlaying(true); + break; + case AudioManager.SCO_AUDIO_STATE_DISCONNECTED: + case AudioManager.SCO_AUDIO_STATE_ERROR: + if(mAudioOutput.isPlaying()) + Toast.makeText(JumbleService.this, R.string.bluetooth_disconnected, Toast.LENGTH_LONG).show(); + mAudioOutput.stopPlaying(); + if(isConnected()) + mAudioOutput.startPlaying(false); + break; + } + } + }; + private AudioInput.AudioInputListener mAudioInputListener = new AudioInput.AudioInputListener() { @Override public void onFrameEncoded(byte[] data, int length, JumbleUDPMessageType messageType) { @@ -266,20 +308,21 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon @Override public void setTalkingState(boolean talking) throws RemoteException { - if(talking) - mAudioInput.startRecording(); - else - mAudioInput.stopRecording(); + if(talking) mAudioInput.startRecording(); + else mAudioInput.stopRecording(); } @Override public boolean isBluetoothAvailable() throws RemoteException { - return false; + AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); + return audioManager.isBluetoothScoOn(); } @Override public void setBluetoothEnabled(boolean enabled) throws RemoteException { - + AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); + if(enabled) audioManager.startBluetoothSco(); + else audioManager.stopBluetoothSco(); } @Override @@ -520,7 +563,14 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon ab.addAllTokens(mAccessTokens); mConnection.sendTCPMessage(ab.build(), JumbleTCPMessageType.Authenticate); - mAudioOutput.startPlaying(); + // Configure audio manager + AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); + int result = audioManager.requestAudioFocus(mAudioFocusChangeListener, + AudioManager.STREAM_MUSIC, + AudioManager.AUDIOFOCUS_GAIN); + // TODO handle result + // This sticky broadcast will initialize the audio output. + registerReceiver(mBluetoothReceiver, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED)); if(mTransmitMode == Constants.TRANSMIT_CONTINUOUS || mTransmitMode == Constants.TRANSMIT_VOICE_ACTIVITY) mAudioInput.startRecording(); @@ -535,9 +585,16 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon @Override public void onConnectionDisconnected() { Log.v(Constants.TAG, "Disconnected"); + unregisterReceiver(mBluetoothReceiver); mAudioOutput.stopPlaying(); - mAudioInput.stopRecording(); + mAudioInput.stopRecordingAndWait(); + mAudioInput.destroy(); + + // Restore audio manager mode + AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); + audioManager.abandonAudioFocus(mAudioFocusChangeListener); + audioManager.stopBluetoothSco(); notifyObservers(new ObserverRunnable() { @Override diff --git a/src/main/java/com/morlunk/jumble/audio/AudioInput.java b/src/main/java/com/morlunk/jumble/audio/AudioInput.java index 9eebe64..cdc3e35 100644 --- a/src/main/java/com/morlunk/jumble/audio/AudioInput.java +++ b/src/main/java/com/morlunk/jumble/audio/AudioInput.java @@ -231,6 +231,8 @@ public class AudioInput extends ProtocolHandler implements Runnable { * Stops the record loop after the current iteration. */ public void stopRecording() { + if(!mRecording) return; + synchronized (mRecordLock) { mRecording = false; mRecordThread = null; @@ -241,10 +243,17 @@ public class AudioInput extends ProtocolHandler implements Runnable { * Stops the record loop and waits on it to finish. * @throws InterruptedException */ - public void stopRecordingAndWait() throws InterruptedException { - stopRecording(); + public void stopRecordingAndWait() { + if(!mRecording) return; + synchronized (mRecordLock) { - mRecordLock.wait(); + mRecording = false; + try { + mRecordThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + mRecordThread = null; } } @@ -418,6 +427,7 @@ public class AudioInput extends ProtocolHandler implements Runnable { mPreprocessState.destroy(); if(mResampler != null) mResampler.destroy(); + mAudioRecord.release(); } @Override diff --git a/src/main/java/com/morlunk/jumble/audio/AudioOutput.java b/src/main/java/com/morlunk/jumble/audio/AudioOutput.java index 8db90cd..32104c4 100644 --- a/src/main/java/com/morlunk/jumble/audio/AudioOutput.java +++ b/src/main/java/com/morlunk/jumble/audio/AudioOutput.java @@ -16,6 +16,10 @@ package com.morlunk.jumble.audio; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; @@ -59,29 +63,49 @@ public class AudioOutput extends ProtocolHandler implements Runnable, AudioOutpu public AudioOutput(JumbleService service) { super(service); + } + + public void startPlaying(boolean scoEnabled) { + if(mRunning) + return; + int bufferSize = AudioTrack.getMinBufferSize(Audio.SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT) * 2; - mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, + mAudioTrack = new AudioTrack(scoEnabled ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_MUSIC, Audio.SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM); - } - - public void startPlaying() { - if(mRunning) - return; mThread = new Thread(this); mThread.start(); } public void stopPlaying() { + if(!mRunning) + return; + mRunning = false; + synchronized (mInactiveLock) { + mInactiveLock.notify(); // Wake inactive lock if active + } + try { + mThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } mThread = null; for(AudioOutputSpeech s : mAudioOutputs.values()) s.destroy(); + + mAudioOutputs.clear(); + mAudioTrack.release(); + mAudioTrack = null; + } + + public boolean isPlaying() { + return mRunning; } @Override |