mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-15 01:57:39 -05:00
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:
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user