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.
This commit is contained in:
denny
2016-02-13 09:07:37 -05:00
parent 1dc0125ef4
commit c670f1d855
4 changed files with 52 additions and 52 deletions

View File

@@ -182,7 +182,8 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
for( AudioOutputConnection connection: mAudioOutputConnections ) for( AudioOutputConnection connection: mAudioOutputConnections )
{ {
if( connection.isConnected() && connection.isInactive() ) if( connection.isInactive() &&
mChannelConnectionMap.containsKey( connection.getSource() ) )
{ {
mChannelConnectionMap.remove( connection.getSource() ); mChannelConnectionMap.remove( connection.getSource() );
connection.disconnect(); connection.disconnect();
@@ -424,7 +425,7 @@ public class AudioManager implements Listener<AudioPacket>, 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 * output and maintains current state information about the audio activity
* received form the source. * received form the source.
*/ */

View File

@@ -1,9 +1,9 @@
package audio.output; package audio.output;
import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.sound.sampled.FloatControl; import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineListener;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -11,18 +11,15 @@ import org.slf4j.LoggerFactory;
import sample.Broadcaster; import sample.Broadcaster;
import sample.Listener; import sample.Listener;
import audio.AudioEvent; import audio.AudioEvent;
import audio.AudioEvent.Type;
import audio.AudioPacket; import audio.AudioPacket;
import audio.metadata.AudioMetadata; import audio.metadata.AudioMetadata;
import controller.ThreadPoolManager; import controller.ThreadPoolManager;
public abstract class AudioOutput implements Listener<AudioPacket> public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
{ {
private final static Logger mLog = LoggerFactory.getLogger( AudioOutput.class ); private final static Logger mLog = LoggerFactory.getLogger( AudioOutput.class );
private AtomicBoolean mSquelched = new AtomicBoolean( false );
private Listener<AudioMetadata> mAudioMetadataListener; private Listener<AudioMetadata> mAudioMetadataListener;
protected Broadcaster<AudioEvent> mAudioEventBroadcaster = new Broadcaster<>(); protected Broadcaster<AudioEvent> mAudioEventBroadcaster = new Broadcaster<>();
protected LinkedTransferQueue<AudioPacket> mBuffer = new LinkedTransferQueue<>(); protected LinkedTransferQueue<AudioPacket> mBuffer = new LinkedTransferQueue<>();
protected ThreadPoolManager mThreadPoolManager; protected ThreadPoolManager mThreadPoolManager;
@@ -74,19 +71,6 @@ public abstract class AudioOutput implements Listener<AudioPacket>
mAudioEventBroadcaster.broadcast( audioEvent ); 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 * Registers a single listener to receive audio start and audio stop events
*/ */

View File

@@ -12,6 +12,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.sound.sampled.BooleanControl; import javax.sound.sampled.BooleanControl;
import javax.sound.sampled.Control; import javax.sound.sampled.Control;
import javax.sound.sampled.FloatControl; import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer; import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.SourceDataLine;
@@ -50,11 +51,9 @@ public class MonoAudioOutput extends AudioOutput
private SourceDataLine mOutput; private SourceDataLine mOutput;
private FloatControl mGainControl; private FloatControl mGainControl;
private BooleanControl mMuteControl; private BooleanControl mMuteControl;
private AudioEvent mAudioStartEvent; private AudioEvent mAudioStartEvent;
private AudioEvent mAudioStopEvent; private AudioEvent mAudioStopEvent;
private AudioEvent mAudioContinuationEvent; private AudioEvent mAudioContinuationEvent;
private ScheduledFuture<?> mProcessorTask; private ScheduledFuture<?> mProcessorTask;
public MonoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer ) public MonoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer )
@@ -76,6 +75,7 @@ public class MonoAudioOutput extends AudioOutput
if( mOutput != null ) if( mOutput != null )
{ {
mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, BUFFER_SIZE ); mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, BUFFER_SIZE );
mOutput.addLineListener( this );
mCanProcessAudio = true; mCanProcessAudio = true;
} }
} }
@@ -162,17 +162,13 @@ public class MonoAudioOutput extends AudioOutput
if( packets.size() > 0 ) if( packets.size() > 0 )
{ {
broadcast( mAudioContinuationEvent );
for( AudioPacket packet: packets ) for( AudioPacket packet: packets )
{ {
broadcast( mAudioContinuationEvent );
if( mCanProcessAudio ) if( mCanProcessAudio )
{ {
if( isSquelched() ) if( packet.getType() == Type.AUDIO )
{
checkStop();
}
else if( packet.getType() == Type.AUDIO )
{ {
float[] samples = packet.getAudioBuffer().getSamples(); float[] samples = packet.getAudioBuffer().getSamples();
@@ -216,24 +212,20 @@ public class MonoAudioOutput extends AudioOutput
private void checkStart() private void checkStart()
{ {
if( mCanProcessAudio && !isSquelched() && if( mCanProcessAudio &&
!mOutput.isRunning() && mOutput.available() <= START_THRESHOLD ) !mOutput.isRunning() && mOutput.available() <= START_THRESHOLD )
{ {
mOutput.start(); mOutput.start();
mAudioEventBroadcaster.broadcast( mAudioStartEvent );
} }
} }
private void checkStop() private void checkStop()
{ {
if( mCanProcessAudio && mOutput.isRunning() && if( mCanProcessAudio && mOutput.isRunning() &&
( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) ) mOutput.available() >= STOP_THRESHOLD )
{ {
mOutput.drain(); mOutput.drain();
mOutput.stop(); mOutput.stop();
mAudioEventBroadcaster.broadcast( mAudioStopEvent );
} }
} }
} }
@@ -272,4 +264,19 @@ public class MonoAudioOutput extends AudioOutput
{ {
return mGainControl != null; 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 );
}
}
} }

View File

@@ -12,6 +12,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.sound.sampled.BooleanControl; import javax.sound.sampled.BooleanControl;
import javax.sound.sampled.Control; import javax.sound.sampled.Control;
import javax.sound.sampled.FloatControl; import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer; import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.SourceDataLine;
@@ -40,9 +41,9 @@ public class StereoAudioOutput extends AudioOutput
private final static int BUFFER_SIZE = 96000; private final static int BUFFER_SIZE = 96000;
/* The queue processor will run every 20 milliseconds checking for inbound /* 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 * 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 SAMPLE_SIZE_40_MS = BUFFER_SIZE / 25;
private final static int START_THRESHOLD = SAMPLE_SIZE_40_MS; private final static int START_THRESHOLD = SAMPLE_SIZE_40_MS;
private final static int STOP_THRESHOLD = BUFFER_SIZE - 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 SourceDataLine mOutput;
private FloatControl mGainControl; private FloatControl mGainControl;
private BooleanControl mMuteControl; private BooleanControl mMuteControl;
private AudioEvent mAudioStartEvent; private AudioEvent mAudioStartEvent;
private AudioEvent mAudioStopEvent; private AudioEvent mAudioStopEvent;
private AudioEvent mAudioContinuationEvent; private AudioEvent mAudioContinuationEvent;
private MixerChannel mMixerChannel; private MixerChannel mMixerChannel;
private ScheduledFuture<?> mProcessorTask; private ScheduledFuture<?> mProcessorTask;
public StereoAudioOutput( ThreadPoolManager threadPoolManager, public StereoAudioOutput( ThreadPoolManager threadPoolManager,
@@ -83,6 +81,7 @@ public class StereoAudioOutput extends AudioOutput
if( mOutput != null ) if( mOutput != null )
{ {
mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO, BUFFER_SIZE ); mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO, BUFFER_SIZE );
mOutput.addLineListener( this );
mCanProcessAudio = true; mCanProcessAudio = true;
} }
} }
@@ -202,17 +201,14 @@ public class StereoAudioOutput extends AudioOutput
if( packets.size() > 0 ) if( packets.size() > 0 )
{ {
broadcast( mAudioContinuationEvent );
for( AudioPacket packet: packets ) for( AudioPacket packet: packets )
{ {
broadcast( mAudioContinuationEvent );
if( mCanProcessAudio ) if( mCanProcessAudio )
{ {
if( isSquelched() ) if( packet.getType() == Type.AUDIO )
{
checkStop();
}
else if( packet.getType() == Type.AUDIO )
{ {
ByteBuffer buffer = convert( ByteBuffer buffer = convert(
packet.getAudioBuffer().getSamples() ); packet.getAudioBuffer().getSamples() );
@@ -245,24 +241,20 @@ public class StereoAudioOutput extends AudioOutput
private void checkStart() private void checkStart()
{ {
if( mCanProcessAudio && !isSquelched() && if( mCanProcessAudio &&
!mOutput.isRunning() && mOutput.available() <= START_THRESHOLD ) !mOutput.isRunning() && mOutput.available() <= START_THRESHOLD )
{ {
mOutput.start(); mOutput.start();
mAudioEventBroadcaster.broadcast( mAudioStartEvent );
} }
} }
private void checkStop() private void checkStop()
{ {
if( mCanProcessAudio && mOutput.isRunning() && if( mCanProcessAudio && mOutput.isRunning() &&
( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) ) mOutput.available() >= STOP_THRESHOLD )
{ {
mOutput.drain(); mOutput.drain();
mOutput.stop(); mOutput.stop();
mAudioEventBroadcaster.broadcast( mAudioStopEvent );
} }
} }
} }
@@ -301,4 +293,20 @@ public class StereoAudioOutput extends AudioOutput
{ {
return mGainControl != null; 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 );
}
}
} }