diff --git a/src/audio/AudioManager.java b/src/audio/AudioManager.java index 1faf0b29..9b1cf38a 100644 --- a/src/audio/AudioManager.java +++ b/src/audio/AudioManager.java @@ -2,7 +2,7 @@ package audio; /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,14 +19,11 @@ package audio; ******************************************************************************/ import java.util.ArrayList; - import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -53,7 +50,6 @@ public class AudioManager implements Listener, IAudioController private static final Logger mLog = LoggerFactory.getLogger( AudioManager.class ); public static final int AUDIO_TIMEOUT = 2000; //2 seconds - public static final int NO_SOURCE = 0; public static final AudioEvent CONFIGURATION_CHANGE_STARTED = new AudioEvent( Type.AUDIO_CONFIGURATION_CHANGE_STARTED, null ); @@ -62,19 +58,19 @@ public class AudioManager implements Listener, IAudioController new AudioEvent( Type.AUDIO_CONFIGURATION_CHANGE_COMPLETE, null ); private LinkedTransferQueue mAudioPacketQueue = new LinkedTransferQueue<>(); - private Map mChannelAssignments = new HashMap<>(); - private int mLowestPrioritySource; + private Map mChannelConnectionMap = new HashMap<>(); + private List mAudioOutputConnections = new ArrayList<>(); + private AudioOutputConnection mLowestPriorityConnection; + private int mAvailableConnectionCount; - private List mAvailableAudioOutputs = new ArrayList<>(); private Map mAudioOutputMap = new HashMap<>(); - private List mChannelNames = new ArrayList<>(); private Broadcaster mControllerBroadcaster = new Broadcaster<>(); private ThreadPoolManager mThreadPoolManager; private ScheduledFuture mProcessingTask; - private MixerChannelConfiguration mMixerChannel; + private MixerChannelConfiguration mMixerChannelConfiguration; /** * Processes all audio produced by the decoding channels and routes audio @@ -92,11 +88,11 @@ public class AudioManager implements Listener, IAudioController if( mixer != null ) { - mMixerChannel = new MixerChannelConfiguration( mixer, MixerChannel.MONO ); + mMixerChannelConfiguration = new MixerChannelConfiguration( mixer, MixerChannel.MONO ); try { - setMixerChannelConfiguration( mMixerChannel ); + setMixerChannelConfiguration( mMixerChannelConfiguration ); } catch ( AudioException e ) { @@ -116,6 +112,15 @@ public class AudioManager implements Listener, IAudioController mThreadPoolManager = null; mProcessingTask = null; + + mChannelConnectionMap.clear(); + + for( AudioOutputConnection connection: mAudioOutputConnections ) + { + connection.dispose(); + } + + mAudioOutputConnections.clear(); } @Override @@ -124,210 +129,47 @@ public class AudioManager implements Listener, IAudioController mAudioPacketQueue.add( packet ); } - public class AudioPacketProcessor implements Runnable - { - @Override - public void run() - { - removeCompletedChannelAssignments(); - - if( mAudioPacketQueue != null ) - { - List packets = new ArrayList(); - - mAudioPacketQueue.drainTo( packets ); - - for( AudioPacket packet: packets ) - { - /* Don't process any packet's marked as do not monitor */ - if( !packet.getAudioMetadata().isDoNotMonitor() ) - { - int source = packet.getAudioMetadata().getSource(); - - if( packet.getType() != AudioPacket.Type.END ) - { - /* Existing channel assignment */ - if( mChannelAssignments.containsKey( source ) ) - { - mChannelAssignments.get( source ).getAudioOutput() - .receive( packet ); - } - /* Start a new channel assignment */ - else if( !mAvailableAudioOutputs.isEmpty() ) - { - AudioOutput channel = mAvailableAudioOutputs.remove( 0 ); - - mChannelAssignments.put( packet.getAudioMetadata().getSource(), - new AudioChannelAssignment( channel, packet.getAudioMetadata().getPriority() ) ); - - updateLowestPriorityAssignment(); - - channel.receive( packet ); - } - /* Override the lowest priority assignment */ - else - { - if( mLowestPrioritySource > -1 ) - { - int priority = packet.getAudioMetadata().getPriority(); - - if( packet.getAudioMetadata().isSelected() || - priority < mChannelAssignments - .get( mLowestPrioritySource ).getPriority() ) - { - AudioChannelAssignment assignment = - mChannelAssignments.remove( mLowestPrioritySource ); - - if( assignment != null ) - { - assignment.setPriority( priority ); - - int overrideSource = packet.getAudioMetadata().getSource(); - - mChannelAssignments.put( overrideSource, assignment ); - - mLowestPrioritySource = overrideSource; - - assignment.getAudioOutput().receive( packet ); - } - else - { - mLowestPrioritySource = -1; - } - } - } - } - } - } - } - } - } - } - /** - * Checks each audio channel assignment and removes any completed channels. + * Checks each audio channel assignment and disconnects any inactive connections */ - private void removeCompletedChannelAssignments() + private void disconnectInactiveChannelAssignments() { - if( !mChannelAssignments.isEmpty() ) + boolean changed = false; + + for( AudioOutputConnection connection: mAudioOutputConnections ) { - Iterator> it = - mChannelAssignments.entrySet().iterator(); - - Entry entry; - - boolean removed = false; - - while( it.hasNext() ) + if( connection.isConnected() && connection.isInactive() ) { - entry = it.next(); - - if( entry.getValue().isComplete() ) - { - mAvailableAudioOutputs.add( entry.getValue().getAudioOutput() ); - entry.getValue().dispose(); - it.remove(); - removed = true; - } - } - - if( removed ) - { - updateLowestPriorityAssignment(); + mChannelConnectionMap.remove( connection.getSource() ); + connection.disconnect(); + changed = true; } } + + if( changed ) + { + updateLowestPriorityAssignment(); + } } /** - * Identifies the lowest priority channel assignment ... the one that can - * be overridden with a higher priority call + * Identifies the lowest priority channel connection */ private void updateLowestPriorityAssignment() { - int lowestPriority = -1; + mLowestPriorityConnection = null; - mLowestPrioritySource = -1; - - for( int source: mChannelAssignments.keySet() ) + for( AudioOutputConnection connection: mAudioOutputConnections ) { - AudioChannelAssignment assignment = mChannelAssignments.get( source ); - - if( assignment.getPriority() > lowestPriority ) + if( connection.isConnected() && + ( mLowestPriorityConnection == null || + mLowestPriorityConnection.getPriority() > connection.getPriority() ) ) { - lowestPriority = assignment.getPriority(); - - mLowestPrioritySource = source; + mLowestPriorityConnection = connection; } } } - public void endAllAudioAssignments() - { - for( AudioOutput output: mAudioOutputMap.values() ) - { - output.receive( new AudioPacket( AudioPacket.Type.END, null ) ); - } - } - - public class AudioChannelAssignment implements Listener - { - private static final long CALL_END_DELAY = 2000; //2 seconds - - private AudioOutput mAudioOutput; - private int mPriority; - private long mAudioStoppedTime = System.currentTimeMillis(); - - public AudioChannelAssignment( AudioOutput audioOutput, int priority ) - { - mAudioOutput = audioOutput; - mPriority = priority; - - mAudioOutput.addAudioEventListener( this ); - } - - public boolean isComplete() - { - return mAudioStoppedTime != Long.MAX_VALUE && - mAudioStoppedTime + CALL_END_DELAY < System.currentTimeMillis(); - } - - public void dispose() - { - mAudioOutput.removeAudioEventListener( this ); - mAudioOutput.reset(); - mAudioOutput = null; - } - - public AudioOutput getAudioOutput() - { - return mAudioOutput; - } - - public int getPriority() - { - return mPriority; - } - - public void setPriority( int priority ) - { - mPriority = priority; - } - - @Override - public void receive( AudioEvent event ) - { - switch( event.getType() ) - { - case AUDIO_STARTED: - case AUDIO_STOPPED: - case AUDIO_CONTINUATION: - mAudioStoppedTime = System.currentTimeMillis(); - break; - default: - break; - } - } - } @Override public void setMixerChannelConfiguration( MixerChannelConfiguration entry ) throws AudioException @@ -342,41 +184,29 @@ public class AudioManager implements Listener, IAudioController { mThreadPoolManager.cancel( mProcessingTask ); } - - endAllAudioAssignments(); - for( Entry e: mChannelAssignments.entrySet() ) - { - e.getValue().getAudioOutput().dispose(); - } - mChannelAssignments.clear(); + disposeCurrentConfiguration(); - for( AudioOutput output: mAvailableAudioOutputs ) - { - output.dispose(); - } - mAvailableAudioOutputs.clear(); - - mChannelNames.clear(); - mAudioOutputMap.clear(); - switch( entry.getMixerChannel() ) { case MONO: - AudioOutput mono = new MonoAudioOutput( entry.getMixer() ); - mAvailableAudioOutputs.add( mono ); - mChannelNames.add( mono.getChannelName() ); + AudioOutput mono = new MonoAudioOutput( mThreadPoolManager, + entry.getMixer() ); + mAudioOutputConnections.add( new AudioOutputConnection( mono ) ); + mAvailableConnectionCount++; mAudioOutputMap.put( mono.getChannelName(), mono ); break; case STEREO: - AudioOutput left = new StereoAudioOutput( entry.getMixer(), MixerChannel.LEFT ); - mAvailableAudioOutputs.add( left ); - mChannelNames.add( left.getChannelName() ); + AudioOutput left = new StereoAudioOutput( mThreadPoolManager, + entry.getMixer(), MixerChannel.LEFT ); + mAudioOutputConnections.add( new AudioOutputConnection( left ) ); + mAvailableConnectionCount++; mAudioOutputMap.put( left.getChannelName(), left ); - AudioOutput right = new StereoAudioOutput( entry.getMixer(), MixerChannel.RIGHT ); - mAvailableAudioOutputs.add( right ); - mChannelNames.add( right.getChannelName() ); + AudioOutput right = new StereoAudioOutput( mThreadPoolManager, + entry.getMixer(), MixerChannel.RIGHT ); + mAudioOutputConnections.add( new AudioOutputConnection( right ) ); + mAvailableConnectionCount++; mAudioOutputMap.put( right.getChannelName(), right ); break; default: @@ -392,10 +222,33 @@ public class AudioManager implements Listener, IAudioController } } + /** + * Clears all channel assignments and terminates all audio outputs in + * preparation for complete shutdown or change to another mixer/channel + * configuration + */ + private void disposeCurrentConfiguration() + { + mChannelConnectionMap.clear(); + + for( AudioOutputConnection connection : mAudioOutputConnections ) + { + connection.dispose(); + } + + mAvailableConnectionCount = 0; + + mAudioOutputConnections.clear(); + + mAudioOutputMap.clear(); + + mLowestPriorityConnection = null; + } + @Override public MixerChannelConfiguration getMixerChannelConfiguration() throws AudioException { - return mMixerChannel; + return mMixerChannelConfiguration; } @Override @@ -426,4 +279,237 @@ public class AudioManager implements Listener, IAudioController { mControllerBroadcaster.removeListener( listener ); } + + /** + * Returns an audio output connection for the packet if one is + * available, or overrides an existing lower priority connection. + * Returns null if no connection is available for the audio packet. + * + * @param packet from a source + * @return an audio output connection or null + */ + private AudioOutputConnection getConnection( AudioPacket packet ) + { + int source = packet.getAudioMetadata().getSource(); + + //Use an existing connection + if( mChannelConnectionMap.containsKey( source ) ) + { + return mChannelConnectionMap.get( source ); + } + + //Connect to and use an available connection + if( mAvailableConnectionCount > 0 ) + { + for( AudioOutputConnection connection: mAudioOutputConnections ) + { + if( connection.isDisconnected() ) + { + connection.connect( source ); + mChannelConnectionMap.put( source, connection ); + return connection; + } + } + } + //Preempt an existing connection and connect it to this higher priority source + else //Check for a channel priority override + { + int priority = packet.getAudioMetadata().getPriority(); + + if( mLowestPriorityConnection != null && + priority < mLowestPriorityConnection.getPriority() ) + { + AudioOutputConnection connection = mLowestPriorityConnection; + + mChannelConnectionMap.remove( connection.getSource() ); + + connection.connect( source ); + + mChannelConnectionMap.put( source, connection ); + + updateLowestPriorityAssignment(); + + return connection; + } + } + + return null; + } + + public class AudioPacketProcessor implements Runnable + { + @Override + public void run() + { + disconnectInactiveChannelAssignments(); + + if( mAudioPacketQueue != null ) + { + List packets = new ArrayList(); + + mAudioPacketQueue.drainTo( packets ); + + for( AudioPacket packet: packets ) + { + /* Don't process any packet's marked as do not monitor */ + if( !packet.getAudioMetadata().isDoNotMonitor() && + packet.getType() == AudioPacket.Type.AUDIO ) + { + AudioOutputConnection connection = getConnection( packet ); + + if( connection != null ) + { + connection.receive( packet ); + } + } + } + } + } + } + + /** + * Audio output connection manages a connection between a soure and an audio + * output and maintains current state information about the audio activity + * received form the source. + */ + public class AudioOutputConnection implements Listener + { + private static final long CALL_END_DELAY = 2000; //2 seconds + private static final int DISCONNECTED = -1; + + private AudioOutput mAudioOutput; + private int mPriority = 0; + private long mLastActivity = System.currentTimeMillis(); + private int mSource = DISCONNECTED; + + public AudioOutputConnection( AudioOutput audioOutput ) + { + mAudioOutput = audioOutput; + mAudioOutput.addAudioEventListener( this ); + } + + public void receive( AudioPacket packet ) + { + if( packet.hasAudioMetadata() && + packet.getAudioMetadata().getSource() == mSource ) + { + int priority = packet.getAudioMetadata().getPriority(); + + if( mPriority != priority ) + { + mPriority = priority; + updateLowestPriorityAssignment(); + } + + updateTimestamp(); + + if( mAudioOutput != null ) + { + mAudioOutput.receive( packet ); + } + } + else + { + if( packet.hasAudioMetadata() ) + { + mLog.error( "Received audio packet from channel source [" + + packet.getAudioMetadata().getSource() + + "] however this assignment is currently connected " + + "to channel source [" + mSource + "]" ); + } + else + { + mLog.error( "Received audio packet with no metadata - " + + "cannot route audio packet" ); + } + } + } + + private void updateTimestamp() + { + mLastActivity = System.currentTimeMillis(); + } + + /** + * Terminates the audio output and prepares this connection for disposal + */ + public void dispose() + { + mAudioOutput.dispose(); + mAudioOutput = null; + } + + /** + * Indicates if this assignment is currently disconnected from a channel source + */ + public boolean isDisconnected() + { + return mSource == DISCONNECTED; + } + + /** + * Indicates if this assignment is currently connected to a channel source + */ + public boolean isConnected() + { + return !isDisconnected(); + } + + /** + * Connects this assignment to the indicated source so that audio + * packets from this source can be sent to the audio output + */ + public void connect( int source ) + { + mSource = source; + updateTimestamp(); + } + + /** + * Currently connected source or -1 if disconnected + */ + public int getSource() + { + return mSource; + } + + /** + * Disconnects this assignment from the source and prevents any audio + * from being routed to the audio output until another source is assigned + */ + public void disconnect() + { + mSource = DISCONNECTED; + } + + /** + * Indicates if audio output is current inactive, meaning that the + * audio output hasn't recently processed any audio packets. + */ + public boolean isInactive() + { + return mLastActivity != Long.MAX_VALUE && + mLastActivity + CALL_END_DELAY < System.currentTimeMillis(); + } + + public int getPriority() + { + return mPriority; + } + + @Override + public void receive( AudioEvent event ) + { + switch( event.getType() ) + { + case AUDIO_STARTED: + case AUDIO_STOPPED: + case AUDIO_CONTINUATION: + updateTimestamp(); + break; + default: + break; + } + } + } } diff --git a/src/audio/output/AudioOutput.java b/src/audio/output/AudioOutput.java index d9506472..c07a7e37 100644 --- a/src/audio/output/AudioOutput.java +++ b/src/audio/output/AudioOutput.java @@ -1,7 +1,6 @@ package audio.output; import java.util.concurrent.LinkedTransferQueue; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.FloatControl; @@ -15,6 +14,7 @@ import audio.AudioEvent; import audio.AudioEvent.Type; import audio.AudioPacket; import audio.metadata.AudioMetadata; +import controller.ThreadPoolManager; public abstract class AudioOutput implements Listener { @@ -25,10 +25,15 @@ public abstract class AudioOutput implements Listener protected Broadcaster mAudioEventBroadcaster = new Broadcaster<>(); protected LinkedTransferQueue mBuffer = new LinkedTransferQueue<>(); - protected ScheduledExecutorService mExecutorService; + protected ThreadPoolManager mThreadPoolManager; protected boolean mCanProcessAudio; protected long mLastActivity; + public AudioOutput( ThreadPoolManager threadPoolManager ) + { + mThreadPoolManager = threadPoolManager; + } + public void reset() { broadcast( new AudioEvent( AudioEvent.Type.AUDIO_STOPPED, @@ -41,14 +46,11 @@ public abstract class AudioOutput implements Listener mBuffer.clear(); - if( mExecutorService != null ) - { - mExecutorService.shutdown(); - } - mAudioEventBroadcaster.dispose(); mAudioEventBroadcaster = null; mAudioMetadataListener = null; + + mThreadPoolManager = null; } public abstract String getChannelName(); diff --git a/src/audio/output/MonoAudioOutput.java b/src/audio/output/MonoAudioOutput.java index b577f241..5ae90b8e 100644 --- a/src/audio/output/MonoAudioOutput.java +++ b/src/audio/output/MonoAudioOutput.java @@ -5,7 +5,7 @@ import java.nio.ByteOrder; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -24,7 +24,8 @@ import audio.AudioEvent; import audio.AudioFormats; import audio.AudioPacket; import audio.AudioPacket.Type; -import controller.NamingThreadFactory; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; /** * Mono Audio output with automatic flow control based on the availability of @@ -54,9 +55,11 @@ public class MonoAudioOutput extends AudioOutput private AudioEvent mAudioStopEvent; private AudioEvent mAudioContinuationEvent; - public MonoAudioOutput( Mixer mixer ) + private ScheduledFuture mProcessorTask; + + public MonoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer ) { - super(); + super( threadPoolManager ); mAudioStartEvent = new AudioEvent( AudioEvent.Type.AUDIO_STARTED, getChannelName() ); @@ -73,15 +76,7 @@ public class MonoAudioOutput extends AudioOutput if( mOutput != null ) { mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, BUFFER_SIZE ); - mCanProcessAudio = true; - - mExecutorService = Executors.newSingleThreadScheduledExecutor( - new NamingThreadFactory( "audio (mono) output" ) ); - - /* Run the queue processor task every 40 milliseconds */ - mExecutorService.scheduleAtFixedRate( new QueueProcessor(), - 0, 40, TimeUnit.MILLISECONDS ); } } catch ( LineUnavailableException e ) @@ -114,12 +109,10 @@ public class MonoAudioOutput extends AudioOutput mixer.getMixerInfo().getName() + "]" ); } - mExecutorService = Executors.newSingleThreadScheduledExecutor( - new NamingThreadFactory( "audio (mono) output" ) ); - /* Run the queue processor task every 40 milliseconds */ - mExecutorService.scheduleAtFixedRate( new QueueProcessor(), - 0, 40, TimeUnit.MILLISECONDS ); + mProcessorTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.AUDIO_PROCESSING, new QueueProcessor(), + 40, TimeUnit.MILLISECONDS ); } } @@ -131,12 +124,23 @@ public class MonoAudioOutput extends AudioOutput public void dispose() { + if( mProcessorTask != null && mThreadPoolManager != null ) + { + mThreadPoolManager.cancel( mProcessorTask ); + } + + mProcessorTask = null; + super.dispose(); if( mOutput != null ) { mOutput.close(); } + + mOutput = null; + mGainControl = null; + mMuteControl = null; } public class QueueProcessor implements Runnable @@ -183,57 +187,12 @@ public class MonoAudioOutput extends AudioOutput { shortBuffer.put( (short)( sample * Short.MAX_VALUE ) ); } - - if( mOutput.available() >= buffer.array().length ) - { - mOutput.write( buffer.array(), 0, - buffer.array().length ); - - checkStart(); - } - else - { - int wrote = 0; - - int toWrite = buffer.array().length; - - while( toWrite > 0 ) - { - int available = mOutput.available(); - - if( available < toWrite ) - { - if( available > 0 ) - { - wrote += mOutput.write( buffer.array(), - wrote, available ); - - checkStart(); - } - else - { - try - { - Thread.sleep( 100 ); - } - catch ( InterruptedException e ) - { - mLog.debug( "Mono Audio Output - interrupted while sleeping awaiting buffer drain" ); - } - } - } - else - { - wrote += mOutput.write( buffer.array(), wrote, - toWrite ); - - checkStart(); - } - - toWrite = buffer.array().length - wrote; - } - } + //This is a blocking write + mOutput.write( buffer.array(), 0, buffer.array().length ); + + checkStart(); + broadcast( packet.getAudioMetadata() ); mLastActivity = System.currentTimeMillis(); diff --git a/src/audio/output/StereoAudioOutput.java b/src/audio/output/StereoAudioOutput.java index b9e3ebc7..9c7c5d9f 100644 --- a/src/audio/output/StereoAudioOutput.java +++ b/src/audio/output/StereoAudioOutput.java @@ -5,7 +5,7 @@ import java.nio.ByteOrder; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -24,7 +24,8 @@ import audio.AudioEvent; import audio.AudioFormats; import audio.AudioPacket; import audio.AudioPacket.Type; -import controller.NamingThreadFactory; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; /** * Mono Audio output in stereo format with automatic flow control based on the @@ -56,9 +57,12 @@ public class StereoAudioOutput extends AudioOutput private MixerChannel mMixerChannel; - public StereoAudioOutput( Mixer mixer, MixerChannel channel ) + private ScheduledFuture mProcessorTask; + + public StereoAudioOutput( ThreadPoolManager threadPoolManager, + Mixer mixer, MixerChannel channel ) { - super(); + super( threadPoolManager ); assert( channel == MixerChannel.LEFT || channel == MixerChannel.RIGHT ); @@ -113,12 +117,10 @@ public class StereoAudioOutput extends AudioOutput mixer.getMixerInfo().getName() + " | " + channel.name() + "]" ); } - mExecutorService = Executors.newSingleThreadScheduledExecutor( - new NamingThreadFactory( "audio (stereo) output" ) ); - /* Run the queue processor task every 40 milliseconds */ - mExecutorService.scheduleAtFixedRate( new QueueProcessor(), - 0, 40, TimeUnit.MILLISECONDS ); + mProcessorTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.AUDIO_PROCESSING, new QueueProcessor(), + 40, TimeUnit.MILLISECONDS ); } } @@ -130,12 +132,23 @@ public class StereoAudioOutput extends AudioOutput public void dispose() { + if( mProcessorTask != null && mThreadPoolManager != null ) + { + mThreadPoolManager.cancel( mProcessorTask ); + } + + mProcessorTask = null; + super.dispose(); if( mOutput != null ) { mOutput.close(); } + + mOutput = null; + mGainControl = null; + mMuteControl = null; } private ByteBuffer convert( float[] samples ) @@ -204,44 +217,10 @@ public class StereoAudioOutput extends AudioOutput ByteBuffer buffer = convert( packet.getAudioBuffer().getSamples() ); - if( mOutput.available() >= buffer.array().length ) - { - mOutput.write( buffer.array(), 0, - buffer.array().length ); + mOutput.write( buffer.array(), 0, + buffer.array().length ); - checkStart(); - } - else - { - int wrote = 0; - - int toWrite = buffer.array().length; - - while( toWrite > 0 ) - { - int available = mOutput.available(); - - if( available < toWrite ) - { - if( available > 0 ) - { - wrote += mOutput.write( buffer.array(), - wrote, available ); - - checkStart(); - } - } - else - { - wrote += mOutput.write( buffer.array(), wrote, - toWrite ); - - checkStart(); - } - - toWrite = buffer.array().length - wrote; - } - } + checkStart(); broadcast( packet.getAudioMetadata() ); diff --git a/src/controller/ThreadPoolManager.java b/src/controller/ThreadPoolManager.java index c5527970..1f727e80 100644 --- a/src/controller/ThreadPoolManager.java +++ b/src/controller/ThreadPoolManager.java @@ -36,24 +36,20 @@ public class ThreadPoolManager private HashMap,ThreadType> mTasks = new HashMap,ThreadType>(); - private ScheduledExecutorService mExecutor; + private ScheduledExecutorService mExecutor = + Executors.newScheduledThreadPool( THREAD_POOL_SIZE, + new NamingThreadFactory( "sdrtrunk" ) ); public ThreadPoolManager() { } - + public ScheduledFuture scheduleFixedRate( ThreadType type, Runnable command, long period, TimeUnit unit ) throws RejectedExecutionException { - if( mExecutor == null ) - { - mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE, - new NamingThreadFactory( "sdrtrunk" ) ); - } - ScheduledFuture task = mExecutor.scheduleAtFixedRate( command, 0, period, unit ); @@ -65,12 +61,6 @@ public class ThreadPoolManager public void scheduleOnce( Runnable command, long delay, TimeUnit unit ) throws RejectedExecutionException { - if( mExecutor == null ) - { - mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE, - new NamingThreadFactory( "sdrtrunk" ) ); - } - mExecutor.schedule( command, delay, unit ); }