From c670f1d855415a778c65791e6b549f242de8effd Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 13 Feb 2016 09:07:37 -0500 Subject: [PATCH] Resolves issue with audio playback state becoming disconnected with AudioManager connection class monitoring of the playback state. Adds a line listener to monitor when the line is started/stopped and responds accordingly. Removes squelch control from mono and stereo audio playback components which was a holdover -- squelch state is now managed within the audio component for each processing chain. --- src/audio/AudioManager.java | 5 +-- src/audio/output/AudioOutput.java | 20 ++---------- src/audio/output/MonoAudioOutput.java | 37 +++++++++++++--------- src/audio/output/StereoAudioOutput.java | 42 +++++++++++++++---------- 4 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/audio/AudioManager.java b/src/audio/AudioManager.java index 809105eb..9d21bf98 100644 --- a/src/audio/AudioManager.java +++ b/src/audio/AudioManager.java @@ -182,7 +182,8 @@ public class AudioManager implements Listener, IAudioController for( AudioOutputConnection connection: mAudioOutputConnections ) { - if( connection.isConnected() && connection.isInactive() ) + if( connection.isInactive() && + mChannelConnectionMap.containsKey( connection.getSource() ) ) { mChannelConnectionMap.remove( connection.getSource() ); connection.disconnect(); @@ -424,7 +425,7 @@ public class AudioManager implements Listener, IAudioController } /** - * Audio output connection manages a connection between a soure and an audio + * Audio output connection manages a connection between a source and an audio * output and maintains current state information about the audio activity * received form the source. */ diff --git a/src/audio/output/AudioOutput.java b/src/audio/output/AudioOutput.java index c07a7e37..74b2e822 100644 --- a/src/audio/output/AudioOutput.java +++ b/src/audio/output/AudioOutput.java @@ -1,9 +1,9 @@ package audio.output; import java.util.concurrent.LinkedTransferQueue; -import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.FloatControl; +import javax.sound.sampled.LineListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,18 +11,15 @@ import org.slf4j.LoggerFactory; import sample.Broadcaster; import sample.Listener; import audio.AudioEvent; -import audio.AudioEvent.Type; import audio.AudioPacket; import audio.metadata.AudioMetadata; import controller.ThreadPoolManager; -public abstract class AudioOutput implements Listener +public abstract class AudioOutput implements Listener, LineListener { private final static Logger mLog = LoggerFactory.getLogger( AudioOutput.class ); - private AtomicBoolean mSquelched = new AtomicBoolean( false ); private Listener mAudioMetadataListener; - protected Broadcaster mAudioEventBroadcaster = new Broadcaster<>(); protected LinkedTransferQueue mBuffer = new LinkedTransferQueue<>(); protected ThreadPoolManager mThreadPoolManager; @@ -74,19 +71,6 @@ public abstract class AudioOutput implements Listener mAudioEventBroadcaster.broadcast( audioEvent ); } - public void setSquelched( boolean squelched ) - { - mSquelched.set( squelched ); - - broadcast( new AudioEvent( squelched ? Type.AUDIO_SQUELCHED : - Type.AUDIO_UNSQUELCHED, getChannelName() ) ); - } - - public boolean isSquelched() - { - return mSquelched.get(); - } - /** * Registers a single listener to receive audio start and audio stop events */ diff --git a/src/audio/output/MonoAudioOutput.java b/src/audio/output/MonoAudioOutput.java index 5ae90b8e..ec1f7816 100644 --- a/src/audio/output/MonoAudioOutput.java +++ b/src/audio/output/MonoAudioOutput.java @@ -12,6 +12,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.BooleanControl; import javax.sound.sampled.Control; import javax.sound.sampled.FloatControl; +import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Mixer; import javax.sound.sampled.SourceDataLine; @@ -50,11 +51,9 @@ public class MonoAudioOutput extends AudioOutput private SourceDataLine mOutput; private FloatControl mGainControl; private BooleanControl mMuteControl; - private AudioEvent mAudioStartEvent; private AudioEvent mAudioStopEvent; private AudioEvent mAudioContinuationEvent; - private ScheduledFuture mProcessorTask; public MonoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer ) @@ -76,6 +75,7 @@ public class MonoAudioOutput extends AudioOutput if( mOutput != null ) { mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, BUFFER_SIZE ); + mOutput.addLineListener( this ); mCanProcessAudio = true; } } @@ -162,17 +162,13 @@ public class MonoAudioOutput extends AudioOutput if( packets.size() > 0 ) { - broadcast( mAudioContinuationEvent ); - for( AudioPacket packet: packets ) { + broadcast( mAudioContinuationEvent ); + if( mCanProcessAudio ) { - if( isSquelched() ) - { - checkStop(); - } - else if( packet.getType() == Type.AUDIO ) + if( packet.getType() == Type.AUDIO ) { float[] samples = packet.getAudioBuffer().getSamples(); @@ -216,24 +212,20 @@ public class MonoAudioOutput extends AudioOutput private void checkStart() { - if( mCanProcessAudio && !isSquelched() && + if( mCanProcessAudio && !mOutput.isRunning() && mOutput.available() <= START_THRESHOLD ) { mOutput.start(); - - mAudioEventBroadcaster.broadcast( mAudioStartEvent ); } } private void checkStop() { if( mCanProcessAudio && mOutput.isRunning() && - ( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) ) + mOutput.available() >= STOP_THRESHOLD ) { mOutput.drain(); mOutput.stop(); - - mAudioEventBroadcaster.broadcast( mAudioStopEvent ); } } } @@ -272,4 +264,19 @@ public class MonoAudioOutput extends AudioOutput { return mGainControl != null; } + + @Override + public void update( LineEvent event ) + { + LineEvent.Type type = event.getType(); + + if( type == LineEvent.Type.START ) + { + mAudioEventBroadcaster.broadcast( mAudioStartEvent ); + } + else if( type == LineEvent.Type.STOP ) + { + mAudioEventBroadcaster.broadcast( mAudioStopEvent ); + } + } } diff --git a/src/audio/output/StereoAudioOutput.java b/src/audio/output/StereoAudioOutput.java index 9c7c5d9f..1c3d1683 100644 --- a/src/audio/output/StereoAudioOutput.java +++ b/src/audio/output/StereoAudioOutput.java @@ -12,6 +12,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.BooleanControl; import javax.sound.sampled.Control; import javax.sound.sampled.FloatControl; +import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Mixer; import javax.sound.sampled.SourceDataLine; @@ -40,9 +41,9 @@ public class StereoAudioOutput extends AudioOutput private final static int BUFFER_SIZE = 96000; /* The queue processor will run every 20 milliseconds checking for inbound - * audio and automatically starting or stopping audio playback. We set the + * audio and automatically start or stop audio playback. We set the * data line available space threshold to start when we're 20 ms away from - * being full, and the stop threshold when 20 ms away from being empty */ + * being full and the stop threshold when 20 ms away from being empty */ private final static int SAMPLE_SIZE_40_MS = BUFFER_SIZE / 25; private final static int START_THRESHOLD = SAMPLE_SIZE_40_MS; private final static int STOP_THRESHOLD = BUFFER_SIZE - SAMPLE_SIZE_40_MS; @@ -50,13 +51,10 @@ public class StereoAudioOutput extends AudioOutput private SourceDataLine mOutput; private FloatControl mGainControl; private BooleanControl mMuteControl; - private AudioEvent mAudioStartEvent; private AudioEvent mAudioStopEvent; private AudioEvent mAudioContinuationEvent; - private MixerChannel mMixerChannel; - private ScheduledFuture mProcessorTask; public StereoAudioOutput( ThreadPoolManager threadPoolManager, @@ -83,6 +81,7 @@ public class StereoAudioOutput extends AudioOutput if( mOutput != null ) { mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO, BUFFER_SIZE ); + mOutput.addLineListener( this ); mCanProcessAudio = true; } } @@ -202,17 +201,14 @@ public class StereoAudioOutput extends AudioOutput if( packets.size() > 0 ) { - broadcast( mAudioContinuationEvent ); for( AudioPacket packet: packets ) { + broadcast( mAudioContinuationEvent ); + if( mCanProcessAudio ) { - if( isSquelched() ) - { - checkStop(); - } - else if( packet.getType() == Type.AUDIO ) + if( packet.getType() == Type.AUDIO ) { ByteBuffer buffer = convert( packet.getAudioBuffer().getSamples() ); @@ -245,24 +241,20 @@ public class StereoAudioOutput extends AudioOutput private void checkStart() { - if( mCanProcessAudio && !isSquelched() && + if( mCanProcessAudio && !mOutput.isRunning() && mOutput.available() <= START_THRESHOLD ) { mOutput.start(); - - mAudioEventBroadcaster.broadcast( mAudioStartEvent ); } } private void checkStop() { if( mCanProcessAudio && mOutput.isRunning() && - ( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) ) + mOutput.available() >= STOP_THRESHOLD ) { mOutput.drain(); mOutput.stop(); - - mAudioEventBroadcaster.broadcast( mAudioStopEvent ); } } } @@ -301,4 +293,20 @@ public class StereoAudioOutput extends AudioOutput { return mGainControl != null; } + + + @Override + public void update( LineEvent event ) + { + LineEvent.Type type = event.getType(); + + if( type == LineEvent.Type.START ) + { + mAudioEventBroadcaster.broadcast( mAudioStartEvent ); + } + else if( type == LineEvent.Type.STOP ) + { + mAudioEventBroadcaster.broadcast( mAudioStopEvent ); + } + } }