mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 00:27:26 -05:00
Merge pull request #127 from DSheirer/#108-thread-pool-cleanup
#108 thread pool cleanup
This commit is contained in:
@@ -1,32 +1,33 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package alias.action;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlSeeAlso;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
|
||||
import message.Message;
|
||||
import alias.Alias;
|
||||
import alias.action.beep.BeepAction;
|
||||
import alias.action.clip.ClipAction;
|
||||
import alias.action.script.ScriptAction;
|
||||
import controller.ThreadPoolManager;
|
||||
import message.Message;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlSeeAlso;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* Alias action defines an action to execute when an alias is detected active.
|
||||
@@ -48,9 +49,7 @@ public abstract class AliasAction
|
||||
* action attached. The alias argument is the parent alias containing the
|
||||
* alias action.
|
||||
*/
|
||||
public abstract void execute( ThreadPoolManager threadPoolManager,
|
||||
Alias alias,
|
||||
Message message );
|
||||
public abstract void execute( ScheduledExecutorService scheduledExecutorService, Alias alias, Message message );
|
||||
|
||||
/**
|
||||
* Dismiss a persistent alias action
|
||||
|
||||
@@ -1,54 +1,69 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package alias.action;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import alias.Alias;
|
||||
import message.Message;
|
||||
import sample.Listener;
|
||||
import alias.Alias;
|
||||
import controller.ThreadPoolManager;
|
||||
import util.ThreadPool;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Manages all alias action events. Each received message is interrogated for
|
||||
* any alias entries and then each alias is interrogated for any alias actions.
|
||||
*
|
||||
* Each alias action is executed and provided a copy of the source message so
|
||||
*
|
||||
* Each alias action is executed and provided a copy of the source message so
|
||||
* that the contents of the message can be used as part of the action.
|
||||
*/
|
||||
public class AliasActionManager implements Listener<Message>
|
||||
{
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
|
||||
public AliasActionManager( ThreadPoolManager threadPoolManager )
|
||||
{
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Message message )
|
||||
{
|
||||
if( message.isValid() )
|
||||
{
|
||||
List<Alias> aliases = message.getAliases();
|
||||
|
||||
if( aliases != null )
|
||||
{
|
||||
for( Alias alias: aliases )
|
||||
{
|
||||
if( alias.hasActions() )
|
||||
{
|
||||
List<AliasAction> actions = alias.getAction();
|
||||
|
||||
for( AliasAction action: actions )
|
||||
{
|
||||
/* Provide access to the thread pool manager in case the
|
||||
public AliasActionManager()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(Message message)
|
||||
{
|
||||
if(message.isValid())
|
||||
{
|
||||
List<Alias> aliases = message.getAliases();
|
||||
|
||||
if(aliases != null)
|
||||
{
|
||||
for(Alias alias : aliases)
|
||||
{
|
||||
if(alias.hasActions())
|
||||
{
|
||||
List<AliasAction> actions = alias.getAction();
|
||||
|
||||
for(AliasAction action : actions)
|
||||
{
|
||||
/* Provide access to the thread pool manager in case the
|
||||
* action needs to setup a timer, and provide the original
|
||||
* message to be used as part of the action (e.g. sending
|
||||
* the message as a text message to a cell phone */
|
||||
action.execute( mThreadPoolManager, alias, message );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
action.execute(ThreadPool.SCHEDULED, alias, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,188 +1,181 @@
|
||||
package alias.action;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
import alias.Alias;
|
||||
import message.Message;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import java.awt.*;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
|
||||
import message.Message;
|
||||
import alias.Alias;
|
||||
import controller.ThreadPoolManager;
|
||||
import controller.ThreadPoolManager.ThreadType;
|
||||
|
||||
public abstract class RecurringAction extends AliasAction
|
||||
{
|
||||
@XmlTransient
|
||||
protected AtomicBoolean mRunning = new AtomicBoolean( false );
|
||||
@XmlTransient
|
||||
private ScheduledFuture<?> mPerpetualAction;
|
||||
|
||||
protected Interval mInterval = Interval.ONCE;
|
||||
protected int mPeriod = 5;
|
||||
|
||||
protected ThreadPoolManager mThreadPoolManager;
|
||||
|
||||
public abstract void performAction( Alias alias, Message message );
|
||||
@XmlTransient
|
||||
protected AtomicBoolean mRunning = new AtomicBoolean(false);
|
||||
@XmlTransient
|
||||
private ScheduledFuture<?> mPerpetualAction;
|
||||
@XmlTransient
|
||||
protected ScheduledExecutorService mScheduledExecutorService;
|
||||
|
||||
@Override
|
||||
public void execute( ThreadPoolManager threadPoolManager,
|
||||
Alias alias,
|
||||
Message message )
|
||||
{
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
protected Interval mInterval = Interval.ONCE;
|
||||
protected int mPeriod = 5;
|
||||
|
||||
if( mRunning.compareAndSet( false, true ) )
|
||||
{
|
||||
switch( mInterval )
|
||||
{
|
||||
case ONCE:
|
||||
performThreadedAction( alias, message );
|
||||
/* Don't reset */
|
||||
break;
|
||||
case DELAYED_RESET:
|
||||
performThreadedAction( alias, message );
|
||||
mThreadPoolManager.scheduleOnce( new ResetTask(),
|
||||
mPeriod, TimeUnit.SECONDS );
|
||||
break;
|
||||
case UNTIL_DISMISSED:
|
||||
mPerpetualAction = mThreadPoolManager
|
||||
.scheduleFixedRate( ThreadType.DECODER,
|
||||
new PerformActionTask( alias, message ),
|
||||
mPeriod, TimeUnit.SECONDS );
|
||||
public abstract void performAction(Alias alias, Message message);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append( "<html><div width='250'>Alias [" );
|
||||
sb.append( alias.getName() );
|
||||
sb.append( "] is active in message [" );
|
||||
sb.append( message.toString() );
|
||||
sb.append( "]</div></html>" );
|
||||
|
||||
final String text = sb.toString();
|
||||
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
JOptionPane.showMessageDialog( null, text,
|
||||
"Alias Alert", JOptionPane.INFORMATION_MESSAGE );
|
||||
|
||||
dismiss( false );
|
||||
|
||||
mThreadPoolManager.scheduleOnce( new ResetTask(),
|
||||
15, TimeUnit.SECONDS );
|
||||
}
|
||||
} );
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void execute(ScheduledExecutorService scheduledExecutorService, Alias alias, Message message)
|
||||
{
|
||||
mScheduledExecutorService = scheduledExecutorService;
|
||||
|
||||
/**
|
||||
* Spawns the performAction() event into a new thread so that it doesn't
|
||||
* delay any decoder actions.
|
||||
*/
|
||||
private void performThreadedAction( final Alias alias, final Message message )
|
||||
{
|
||||
mThreadPoolManager.scheduleOnce( new Runnable() {
|
||||
if(mRunning.compareAndSet(false, true))
|
||||
{
|
||||
switch(mInterval)
|
||||
{
|
||||
case ONCE:
|
||||
performThreadedAction(alias, message);
|
||||
/* Don't reset */
|
||||
break;
|
||||
case DELAYED_RESET:
|
||||
performThreadedAction(alias, message);
|
||||
mScheduledExecutorService.schedule(new ResetTask(), mPeriod, TimeUnit.SECONDS);
|
||||
break;
|
||||
case UNTIL_DISMISSED:
|
||||
mPerpetualAction = mScheduledExecutorService.scheduleAtFixedRate(
|
||||
new PerformActionTask(alias, message), mPeriod, mPeriod, TimeUnit.SECONDS);
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
performAction( alias, message );
|
||||
}}, 0, TimeUnit.SECONDS );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss( boolean reset )
|
||||
{
|
||||
if( mPerpetualAction != null )
|
||||
{
|
||||
mPerpetualAction.cancel( true );
|
||||
|
||||
mPerpetualAction = null;
|
||||
}
|
||||
|
||||
if( reset )
|
||||
{
|
||||
mRunning.set( false );
|
||||
}
|
||||
}
|
||||
|
||||
@XmlAttribute
|
||||
public int getPeriod()
|
||||
{
|
||||
return mPeriod;
|
||||
}
|
||||
|
||||
public void setPeriod( int period )
|
||||
{
|
||||
mPeriod = period;
|
||||
}
|
||||
|
||||
@XmlAttribute
|
||||
public Interval getInterval()
|
||||
{
|
||||
return mInterval;
|
||||
}
|
||||
|
||||
public void setInterval( Interval interval )
|
||||
{
|
||||
mInterval = interval;
|
||||
|
||||
mRunning.set( false );
|
||||
}
|
||||
|
||||
public enum Interval
|
||||
{
|
||||
ONCE( "Once" ),
|
||||
DELAYED_RESET( "Once, Reset After Delay" ),
|
||||
UNTIL_DISMISSED( "Until Dismissed" );
|
||||
|
||||
private String mLabel;
|
||||
|
||||
private Interval( String label )
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
}
|
||||
|
||||
public class PerformActionTask implements Runnable
|
||||
{
|
||||
private Alias mAlias;
|
||||
private Message mMessage;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<html><div width='250'>Alias [");
|
||||
sb.append(alias.getName());
|
||||
sb.append("] is active in message [");
|
||||
sb.append(message.toString());
|
||||
sb.append("]</div></html>");
|
||||
|
||||
public PerformActionTask( Alias alias, Message message )
|
||||
{
|
||||
mAlias = alias;
|
||||
mMessage = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
final String text = sb.toString();
|
||||
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
JOptionPane.showMessageDialog(null, text,
|
||||
"Alias Alert", JOptionPane.INFORMATION_MESSAGE);
|
||||
|
||||
dismiss(false);
|
||||
|
||||
mScheduledExecutorService.schedule(new ResetTask(), 15, TimeUnit.SECONDS);
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns the performAction() event into a new thread so that it doesn't
|
||||
* delay any decoder actions.
|
||||
*/
|
||||
private void performThreadedAction(final Alias alias, final Message message)
|
||||
{
|
||||
mScheduledExecutorService.schedule(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
performAction(alias, message);
|
||||
}
|
||||
}, 0, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss(boolean reset)
|
||||
{
|
||||
if(mPerpetualAction != null)
|
||||
{
|
||||
mPerpetualAction.cancel(true);
|
||||
|
||||
mPerpetualAction = null;
|
||||
}
|
||||
|
||||
if(reset)
|
||||
{
|
||||
mRunning.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlAttribute
|
||||
public int getPeriod()
|
||||
{
|
||||
return mPeriod;
|
||||
}
|
||||
|
||||
public void setPeriod(int period)
|
||||
{
|
||||
mPeriod = period;
|
||||
}
|
||||
|
||||
@XmlAttribute
|
||||
public Interval getInterval()
|
||||
{
|
||||
return mInterval;
|
||||
}
|
||||
|
||||
public void setInterval(Interval interval)
|
||||
{
|
||||
mInterval = interval;
|
||||
|
||||
mRunning.set(false);
|
||||
}
|
||||
|
||||
public enum Interval
|
||||
{
|
||||
ONCE("Once"),
|
||||
DELAYED_RESET("Once, Reset After Delay"),
|
||||
UNTIL_DISMISSED("Until Dismissed");
|
||||
|
||||
private String mLabel;
|
||||
|
||||
private Interval(String label)
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
}
|
||||
|
||||
public class PerformActionTask implements Runnable
|
||||
{
|
||||
private Alias mAlias;
|
||||
private Message mMessage;
|
||||
|
||||
public PerformActionTask(Alias alias, Message message)
|
||||
{
|
||||
mAlias = alias;
|
||||
mMessage = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
/* Don't use the performThreadedAction() method, since this is
|
||||
* already running in a separate thread */
|
||||
performAction( mAlias, mMessage );
|
||||
}
|
||||
}
|
||||
|
||||
public class ResetTask implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
mRunning.set( false );
|
||||
}
|
||||
}
|
||||
performAction(mAlias, mMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public class ResetTask implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
mRunning.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,6 @@ import audio.AudioEvent.Type;
|
||||
import audio.output.AudioOutput;
|
||||
import audio.output.MonoAudioOutput;
|
||||
import audio.output.StereoAudioOutput;
|
||||
import controller.ThreadPoolManager;
|
||||
import controller.ThreadPoolManager.ThreadType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import properties.SystemProperties;
|
||||
@@ -32,6 +30,7 @@ import sample.Listener;
|
||||
import source.mixer.MixerChannel;
|
||||
import source.mixer.MixerChannelConfiguration;
|
||||
import source.mixer.MixerManager;
|
||||
import util.ThreadPool;
|
||||
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.Mixer;
|
||||
@@ -70,7 +69,6 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
|
||||
private Broadcaster<AudioEvent> mControllerBroadcaster = new Broadcaster<>();
|
||||
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
private ScheduledFuture<?> mProcessingTask;
|
||||
private MixerManager mMixerManager;
|
||||
private MixerChannelConfiguration mMixerChannelConfiguration;
|
||||
@@ -81,9 +79,8 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
* packets to any combination of outputs based on any alias audio routing
|
||||
* options specified by the user.
|
||||
*/
|
||||
public AudioManager(ThreadPoolManager manager, MixerManager mixerManager)
|
||||
public AudioManager(MixerManager mixerManager)
|
||||
{
|
||||
mThreadPoolManager = manager;
|
||||
mMixerManager = mixerManager;
|
||||
|
||||
loadSettings();
|
||||
@@ -145,14 +142,13 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
if(mThreadPoolManager != null && mProcessingTask != null)
|
||||
if(mProcessingTask != null)
|
||||
{
|
||||
mThreadPoolManager.cancel(mProcessingTask);
|
||||
mProcessingTask.cancel(true);
|
||||
}
|
||||
|
||||
mAudioPacketQueue.clear();
|
||||
|
||||
mThreadPoolManager = null;
|
||||
mProcessingTask = null;
|
||||
|
||||
mChannelConnectionMap.clear();
|
||||
@@ -224,9 +220,9 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
{
|
||||
mControllerBroadcaster.broadcast(CONFIGURATION_CHANGE_STARTED);
|
||||
|
||||
if(mThreadPoolManager != null && mProcessingTask != null)
|
||||
if(mProcessingTask != null)
|
||||
{
|
||||
mThreadPoolManager.cancel(mProcessingTask);
|
||||
mProcessingTask.cancel(true);
|
||||
}
|
||||
|
||||
disposeCurrentConfiguration();
|
||||
@@ -234,19 +230,18 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
switch(entry.getMixerChannel())
|
||||
{
|
||||
case MONO:
|
||||
AudioOutput mono = new MonoAudioOutput(mThreadPoolManager,
|
||||
entry.getMixer());
|
||||
AudioOutput mono = new MonoAudioOutput(entry.getMixer());
|
||||
mAudioOutputConnections.add(new AudioOutputConnection(mono));
|
||||
mAvailableConnectionCount++;
|
||||
mAudioOutputMap.put(mono.getChannelName(), mono);
|
||||
break;
|
||||
case STEREO:
|
||||
AudioOutput left = new StereoAudioOutput(mThreadPoolManager, entry.getMixer(), MixerChannel.LEFT);
|
||||
AudioOutput left = new StereoAudioOutput(entry.getMixer(), MixerChannel.LEFT);
|
||||
mAudioOutputConnections.add(new AudioOutputConnection(left));
|
||||
mAvailableConnectionCount++;
|
||||
mAudioOutputMap.put(left.getChannelName(), left);
|
||||
|
||||
AudioOutput right = new StereoAudioOutput(mThreadPoolManager, entry.getMixer(), MixerChannel.RIGHT);
|
||||
AudioOutput right = new StereoAudioOutput(entry.getMixer(), MixerChannel.RIGHT);
|
||||
mAudioOutputConnections.add(new AudioOutputConnection(right));
|
||||
mAvailableConnectionCount++;
|
||||
mAudioOutputMap.put(right.getChannelName(), right);
|
||||
@@ -256,9 +251,8 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
+ "configuration: " + entry.getMixerChannel());
|
||||
}
|
||||
|
||||
mProcessingTask = mThreadPoolManager.scheduleFixedRate(
|
||||
ThreadType.AUDIO_PROCESSING, new AudioPacketProcessor(), 15,
|
||||
TimeUnit.MILLISECONDS);
|
||||
mProcessingTask = ThreadPool.SCHEDULED.scheduleAtFixedRate(new AudioPacketProcessor(),
|
||||
0, 15, TimeUnit.MILLISECONDS);
|
||||
|
||||
mControllerBroadcaster.broadcast(CONFIGURATION_CHANGE_COMPLETE);
|
||||
|
||||
|
||||
@@ -20,10 +20,10 @@ package audio.broadcast;
|
||||
|
||||
import audio.convert.ISilenceGenerator;
|
||||
import channel.metadata.Metadata;
|
||||
import controller.ThreadPoolManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sample.Listener;
|
||||
import util.ThreadPool;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -40,7 +40,6 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
||||
|
||||
public static final int PROCESSOR_RUN_INTERVAL_MS = 1000;
|
||||
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
private ScheduledFuture mScheduledTask;
|
||||
|
||||
private RecordingQueueProcessor mRecordingQueueProcessor = new RecordingQueueProcessor();
|
||||
@@ -77,19 +76,13 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
||||
* The last audio packet's metadata is automatically attached to the closed audio recording when it is enqueued for
|
||||
* broadcast. That metadata will be updated on the remote server once the audio recording is opened for streaming.
|
||||
*/
|
||||
public AudioBroadcaster(ThreadPoolManager threadPoolManager, BroadcastConfiguration broadcastConfiguration)
|
||||
public AudioBroadcaster(BroadcastConfiguration broadcastConfiguration)
|
||||
{
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
mBroadcastConfiguration = broadcastConfiguration;
|
||||
mDelay = mBroadcastConfiguration.getDelay();
|
||||
mSilenceGenerator = BroadcastFactory.getSilenceGenerator(broadcastConfiguration.getBroadcastFormat());
|
||||
}
|
||||
|
||||
protected ThreadPoolManager getThreadPoolManager()
|
||||
{
|
||||
return mThreadPoolManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast binary audio data frames or sequences.
|
||||
*/
|
||||
@@ -129,11 +122,8 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
||||
{
|
||||
if(mScheduledTask == null)
|
||||
{
|
||||
if(mThreadPoolManager != null)
|
||||
{
|
||||
mScheduledTask = mThreadPoolManager.scheduleFixedRate(ThreadPoolManager.ThreadType.AUDIO_PROCESSING,
|
||||
mRecordingQueueProcessor, PROCESSOR_RUN_INTERVAL_MS, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
ThreadPool.SCHEDULED.scheduleAtFixedRate(mRecordingQueueProcessor, 0,
|
||||
PROCESSOR_RUN_INTERVAL_MS, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,9 +135,9 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
||||
{
|
||||
if(mStreaming.compareAndSet(true, false))
|
||||
{
|
||||
if(mThreadPoolManager != null && mScheduledTask != null)
|
||||
if(mScheduledTask != null)
|
||||
{
|
||||
mThreadPoolManager.cancel(mScheduledTask);
|
||||
mScheduledTask.cancel(true);
|
||||
}
|
||||
|
||||
disconnect();
|
||||
|
||||
@@ -37,7 +37,6 @@ import audio.convert.IAudioConverter;
|
||||
import audio.convert.ISilenceGenerator;
|
||||
import audio.convert.MP3AudioConverter;
|
||||
import audio.convert.MP3SilenceGenerator;
|
||||
import controller.ThreadPoolManager;
|
||||
import gui.editor.Editor;
|
||||
import gui.editor.EmptyEditor;
|
||||
import icon.IconManager;
|
||||
@@ -45,25 +44,23 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import record.AudioRecorder;
|
||||
import record.mp3.MP3Recorder;
|
||||
import settings.SettingsManager;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class BroadcastFactory
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( BroadcastFactory.class );
|
||||
private final static Logger mLog = LoggerFactory.getLogger(BroadcastFactory.class);
|
||||
|
||||
public static final int MP3_MONO_16_KHZ_BITRATE = 16;
|
||||
public static final boolean MP3_CONSTANT_BITRATE = false;
|
||||
|
||||
/**
|
||||
* Creates an audio streaming broadcaster for the configuration
|
||||
* @param threadPoolManager for creating an audio broadcasting connection thread
|
||||
*
|
||||
* @param configuration describing the server and audio types
|
||||
* @return configured broadcaster or null
|
||||
*/
|
||||
public static AudioBroadcaster getBroadcaster(ThreadPoolManager threadPoolManager,
|
||||
BroadcastConfiguration configuration)
|
||||
public static AudioBroadcaster getBroadcaster(BroadcastConfiguration configuration)
|
||||
{
|
||||
if(configuration != null)
|
||||
{
|
||||
@@ -74,20 +71,15 @@ public class BroadcastFactory
|
||||
switch(configuration.getBroadcastServerType())
|
||||
{
|
||||
case BROADCASTIFY:
|
||||
return new IcecastTCPAudioBroadcaster(threadPoolManager,
|
||||
(BroadcastifyConfiguration)configuration);
|
||||
return new IcecastTCPAudioBroadcaster((BroadcastifyConfiguration) configuration);
|
||||
case ICECAST_TCP:
|
||||
return new IcecastTCPAudioBroadcaster(threadPoolManager,
|
||||
(IcecastTCPConfiguration)configuration);
|
||||
return new IcecastTCPAudioBroadcaster((IcecastTCPConfiguration) configuration);
|
||||
case ICECAST_HTTP:
|
||||
return new IcecastHTTPAudioBroadcaster(threadPoolManager,
|
||||
(IcecastHTTPConfiguration)configuration);
|
||||
return new IcecastHTTPAudioBroadcaster((IcecastHTTPConfiguration) configuration);
|
||||
case SHOUTCAST_V1:
|
||||
return new ShoutcastV1AudioBroadcaster(threadPoolManager,
|
||||
(ShoutcastV1Configuration)configuration);
|
||||
return new ShoutcastV1AudioBroadcaster((ShoutcastV1Configuration) configuration);
|
||||
case SHOUTCAST_V2:
|
||||
return new ShoutcastV2AudioBroadcaster(threadPoolManager,
|
||||
(ShoutcastV2Configuration)configuration);
|
||||
return new ShoutcastV2AudioBroadcaster((ShoutcastV2Configuration) configuration);
|
||||
case UNKNOWN:
|
||||
default:
|
||||
mLog.info("Unrecognized broadcastAudio configuration: " + configuration.getBroadcastFormat().name());
|
||||
@@ -101,6 +93,7 @@ public class BroadcastFactory
|
||||
|
||||
/**
|
||||
* Creates an audio convert to convert from 8 kHz PCM audio to the specified format
|
||||
*
|
||||
* @param configuration containing the requested output audio format
|
||||
* @return audio convert or null
|
||||
*/
|
||||
@@ -149,6 +142,7 @@ public class BroadcastFactory
|
||||
|
||||
/**
|
||||
* Constructs an editor for the specified broadcastAudio configuration
|
||||
*
|
||||
* @param configuration to modify or view
|
||||
* @param broadcastModel model for broadcastAudio configurations
|
||||
* @return an editor for the specified broadcastAudio configuration
|
||||
@@ -160,10 +154,10 @@ public class BroadcastFactory
|
||||
{
|
||||
Editor<BroadcastConfiguration> editor;
|
||||
|
||||
switch (configuration.getBroadcastServerType())
|
||||
switch(configuration.getBroadcastServerType())
|
||||
{
|
||||
case BROADCASTIFY:
|
||||
editor = new BroadcastifyConfigurationEditor(broadcastModel, aliasModel, iconManager);
|
||||
editor = new BroadcastifyConfigurationEditor(broadcastModel, aliasModel, iconManager);
|
||||
break;
|
||||
case ICECAST_TCP:
|
||||
editor = new IcecastTCPConfigurationEditor(broadcastModel, aliasModel, iconManager);
|
||||
@@ -192,7 +186,7 @@ public class BroadcastFactory
|
||||
*/
|
||||
public static AudioRecorder getAudioRecorder(Path path, BroadcastFormat broadcastFormat)
|
||||
{
|
||||
switch (broadcastFormat)
|
||||
switch(broadcastFormat)
|
||||
{
|
||||
case MP3:
|
||||
return new MP3Recorder(path);
|
||||
@@ -204,7 +198,7 @@ public class BroadcastFactory
|
||||
|
||||
public static ISilenceGenerator getSilenceGenerator(BroadcastFormat format)
|
||||
{
|
||||
switch (format)
|
||||
switch(format)
|
||||
{
|
||||
case MP3:
|
||||
return new MP3SilenceGenerator();
|
||||
|
||||
@@ -21,13 +21,13 @@ package audio.broadcast;
|
||||
import alias.id.broadcast.BroadcastChannel;
|
||||
import audio.AudioPacket;
|
||||
import channel.metadata.Metadata;
|
||||
import controller.ThreadPoolManager;
|
||||
import icon.IconManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import properties.SystemProperties;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import util.ThreadPool;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
@@ -66,7 +66,6 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
||||
|
||||
private Map<String,BroadcastConfiguration> mBroadcastConfigurationMap = new HashMap<>();
|
||||
private Map<String,AudioBroadcaster> mBroadcasterMap = new HashMap<>();
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
private IconManager mIconManager;
|
||||
private StreamManager mStreamManager;
|
||||
private Broadcaster<BroadcastEvent> mBroadcastEventBroadcaster = new Broadcaster<>();
|
||||
@@ -74,17 +73,16 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
||||
/**
|
||||
* Model for managing Broadcast configurations and any associated broadcaster instances.
|
||||
*/
|
||||
public BroadcastModel(ThreadPoolManager threadPoolManager, IconManager iconManager)
|
||||
public BroadcastModel(IconManager iconManager)
|
||||
{
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
mIconManager = iconManager;
|
||||
mStreamManager = new StreamManager(threadPoolManager, new CompletedRecordingListener(), BroadcastFormat.MP3,
|
||||
mStreamManager = new StreamManager(new CompletedRecordingListener(), BroadcastFormat.MP3,
|
||||
SystemProperties.getInstance().getApplicationFolder(TEMPORARY_STREAM_DIRECTORY));
|
||||
mStreamManager.start();
|
||||
|
||||
//Monitor to remove temporary recording files that have been streamed by all audio broadcasters
|
||||
mThreadPoolManager.scheduleFixedRate(ThreadPoolManager.ThreadType.AUDIO_RECORDING,
|
||||
new RecordingDeletionMonitor(), 15l, TimeUnit.SECONDS);
|
||||
ThreadPool.SCHEDULED.scheduleAtFixedRate(new RecordingDeletionMonitor(),
|
||||
15l, 15l, TimeUnit.SECONDS);
|
||||
|
||||
removeOrphanedTemporaryRecordings();
|
||||
}
|
||||
@@ -293,8 +291,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
||||
broadcastConfiguration.isValid() &&
|
||||
!mBroadcasterMap.containsKey(broadcastConfiguration.getName()))
|
||||
{
|
||||
AudioBroadcaster audioBroadcaster = BroadcastFactory.getBroadcaster(mThreadPoolManager,
|
||||
broadcastConfiguration);
|
||||
AudioBroadcaster audioBroadcaster = BroadcastFactory.getBroadcaster(broadcastConfiguration);
|
||||
|
||||
if(audioBroadcaster != null)
|
||||
{
|
||||
@@ -403,7 +400,8 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
||||
deleteBroadcaster(previousChannelName);
|
||||
|
||||
//Delay restarting the broadcaster to allow remote server time to cleanup
|
||||
mThreadPoolManager.scheduleOnce(new DelayedBroadcasterStartup(broadcastConfiguration), 1, TimeUnit.SECONDS);
|
||||
ThreadPool.SCHEDULED.schedule(new DelayedBroadcasterStartup(broadcastConfiguration),
|
||||
1, TimeUnit.SECONDS);
|
||||
break;
|
||||
case CONFIGURATION_DELETE:
|
||||
deleteBroadcaster(broadcastEvent.getBroadcastConfiguration().getName());
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
package audio.broadcast;
|
||||
|
||||
import audio.AudioPacket;
|
||||
import controller.ThreadPoolManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import record.AudioRecorder;
|
||||
import sample.Listener;
|
||||
import util.ThreadPool;
|
||||
import util.TimeStamp;
|
||||
|
||||
import java.nio.file.Files;
|
||||
@@ -44,7 +44,6 @@ public class StreamManager implements Listener<AudioPacket>
|
||||
|
||||
private static AtomicInteger sNextRecordingNumber = new AtomicInteger();
|
||||
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
private Listener<AudioRecording> mAudioRecordingListener;
|
||||
private BroadcastFormat mBroadcastFormat;
|
||||
private Path mTempDirectory;
|
||||
@@ -61,17 +60,13 @@ public class StreamManager implements Listener<AudioPacket>
|
||||
*
|
||||
* Completed streamable audio recordings are nominated to the output listener (for broadcast) upon completion
|
||||
*
|
||||
* @param threadPoolManager for scheduling runnables
|
||||
* @param listener to receive completed audio recordings
|
||||
* @param tempDirectory where to store temporary audio recordings
|
||||
*/
|
||||
public StreamManager(ThreadPoolManager threadPoolManager, Listener<AudioRecording> listener,
|
||||
BroadcastFormat broadcastFormat, Path tempDirectory)
|
||||
public StreamManager(Listener<AudioRecording> listener, BroadcastFormat broadcastFormat, Path tempDirectory)
|
||||
{
|
||||
assert (tempDirectory != null && Files.isDirectory(tempDirectory));
|
||||
assert (mThreadPoolManager != null);
|
||||
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
mAudioRecordingListener = listener;
|
||||
mBroadcastFormat = broadcastFormat;
|
||||
mTempDirectory = tempDirectory;
|
||||
@@ -90,8 +85,8 @@ public class StreamManager implements Listener<AudioPacket>
|
||||
mRecorderMonitor = new RecorderMonitor();
|
||||
}
|
||||
|
||||
mRecorderMonitorFuture = mThreadPoolManager.scheduleFixedRate(ThreadPoolManager.ThreadType.AUDIO_PROCESSING,
|
||||
mRecorderMonitor, 2, TimeUnit.SECONDS);
|
||||
mRecorderMonitorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(mRecorderMonitor,
|
||||
0,2, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +140,7 @@ public class StreamManager implements Listener<AudioPacket>
|
||||
{
|
||||
AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(),
|
||||
mBroadcastFormat);
|
||||
recorder.start(mThreadPoolManager.getScheduledExecutorService());
|
||||
recorder.start(ThreadPool.SCHEDULED);
|
||||
recorder.receive(audioPacket);
|
||||
mStreamRecorders.put(channelMetadataID, recorder);
|
||||
}
|
||||
|
||||
@@ -21,15 +21,14 @@ package audio.broadcast.icecast;
|
||||
import audio.broadcast.AudioBroadcaster;
|
||||
import audio.broadcast.BroadcastConfiguration;
|
||||
import audio.broadcast.IBroadcastMetadataUpdater;
|
||||
import controller.ThreadPoolManager;
|
||||
|
||||
public abstract class IcecastAudioBroadcaster extends AudioBroadcaster
|
||||
{
|
||||
private IBroadcastMetadataUpdater mMetadataUpdater;
|
||||
|
||||
public IcecastAudioBroadcaster(ThreadPoolManager threadPoolManager, BroadcastConfiguration broadcastConfiguration)
|
||||
public IcecastAudioBroadcaster(BroadcastConfiguration broadcastConfiguration)
|
||||
{
|
||||
super(threadPoolManager, broadcastConfiguration);
|
||||
super(broadcastConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,7 +36,7 @@ public abstract class IcecastAudioBroadcaster extends AudioBroadcaster
|
||||
*/
|
||||
protected IcecastConfiguration getConfiguration()
|
||||
{
|
||||
return (IcecastConfiguration)getBroadcastConfiguration();
|
||||
return (IcecastConfiguration) getBroadcastConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,7 +44,7 @@ public abstract class IcecastAudioBroadcaster extends AudioBroadcaster
|
||||
{
|
||||
if(mMetadataUpdater == null)
|
||||
{
|
||||
mMetadataUpdater = new IcecastBroadcastMetadataUpdater(getThreadPoolManager(), getConfiguration());
|
||||
mMetadataUpdater = new IcecastBroadcastMetadataUpdater(getConfiguration());
|
||||
}
|
||||
|
||||
return mMetadataUpdater;
|
||||
|
||||
@@ -20,7 +20,6 @@ package audio.broadcast.icecast;
|
||||
|
||||
import audio.broadcast.IBroadcastMetadataUpdater;
|
||||
import channel.metadata.Metadata;
|
||||
import controller.ThreadPoolManager;
|
||||
import org.apache.mina.core.future.ConnectFuture;
|
||||
import org.apache.mina.core.service.IoHandlerAdapter;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
@@ -33,6 +32,7 @@ import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import properties.SystemProperties;
|
||||
import util.ThreadPool;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.ConnectException;
|
||||
@@ -50,7 +50,6 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
|
||||
private final static Logger mLog = LoggerFactory.getLogger(IcecastBroadcastMetadataUpdater.class);
|
||||
private final static String UTF8 = "UTF-8";
|
||||
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
private IcecastConfiguration mIcecastConfiguration;
|
||||
|
||||
private NioSocketConnector mSocketConnector;
|
||||
@@ -65,9 +64,8 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
|
||||
* broadcaster. When multiple metadata updates are received prior to completion of the current ongoing update
|
||||
* sequence, those updates will be queued and processed in the order received.
|
||||
*/
|
||||
public IcecastBroadcastMetadataUpdater(ThreadPoolManager threadPoolManager, IcecastConfiguration icecastConfiguration)
|
||||
public IcecastBroadcastMetadataUpdater(IcecastConfiguration icecastConfiguration)
|
||||
{
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
mIcecastConfiguration = icecastConfiguration;
|
||||
}
|
||||
|
||||
@@ -128,7 +126,7 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
|
||||
|
||||
if(updateRequest != null)
|
||||
{
|
||||
mThreadPoolManager.scheduleOnce(new Runnable()
|
||||
ThreadPool.SCHEDULED.schedule(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
|
||||
@@ -20,7 +20,6 @@ package audio.broadcast.icecast;
|
||||
|
||||
import audio.broadcast.BroadcastState;
|
||||
import audio.convert.MP3AudioConverter;
|
||||
import controller.ThreadPoolManager;
|
||||
import org.apache.mina.core.RuntimeIoException;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.future.ConnectFuture;
|
||||
@@ -38,6 +37,7 @@ import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import properties.SystemProperties;
|
||||
import util.ThreadPool;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -48,12 +48,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger( IcecastHTTPAudioBroadcaster.class );
|
||||
private static final Logger mLog = LoggerFactory.getLogger(IcecastHTTPAudioBroadcaster.class);
|
||||
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
|
||||
|
||||
private NioSocketConnector mSocketConnector;
|
||||
private IoSession mStreamingSession = null;
|
||||
private Map<String, String> mHTTPHeaders;
|
||||
private Map<String,String> mHTTPHeaders;
|
||||
|
||||
private long mLastConnectionAttempt = 0;
|
||||
private AtomicBoolean mConnecting = new AtomicBoolean();
|
||||
@@ -69,9 +69,9 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
*
|
||||
* @param configuration for the Icecast stream
|
||||
*/
|
||||
public IcecastHTTPAudioBroadcaster(ThreadPoolManager threadPoolManager, IcecastHTTPConfiguration configuration)
|
||||
public IcecastHTTPAudioBroadcaster(IcecastHTTPConfiguration configuration)
|
||||
{
|
||||
super(threadPoolManager, configuration);
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +101,7 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
private boolean connect()
|
||||
{
|
||||
if(!connected() && canConnect() &&
|
||||
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
|
||||
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
|
||||
mConnecting.compareAndSet(false, true))
|
||||
{
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
@@ -159,7 +159,7 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
}
|
||||
};
|
||||
|
||||
getThreadPoolManager().scheduleOnce(runnable, 0l, TimeUnit.SECONDS);
|
||||
ThreadPool.SCHEDULED.schedule(runnable, 0l, TimeUnit.SECONDS);
|
||||
|
||||
}
|
||||
|
||||
@@ -264,10 +264,10 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
{
|
||||
if(throwable instanceof ProtocolDecoderException)
|
||||
{
|
||||
Throwable cause = ((ProtocolDecoderException)throwable).getCause();
|
||||
Throwable cause = ((ProtocolDecoderException) throwable).getCause();
|
||||
|
||||
if(cause instanceof HttpException &&
|
||||
((HttpException)cause).getStatusCode() == HttpStatus.CLIENT_ERROR_LENGTH_REQUIRED.code())
|
||||
((HttpException) cause).getStatusCode() == HttpStatus.CLIENT_ERROR_LENGTH_REQUIRED.code())
|
||||
{
|
||||
//Ignore - Icecast 2.4 sometimes doesn't send any headers with their HTTP responses. Mina expects a
|
||||
//content-length for HTTP responses.
|
||||
@@ -294,7 +294,7 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
{
|
||||
if(object instanceof DefaultHttpResponse)
|
||||
{
|
||||
DefaultHttpResponse response = (DefaultHttpResponse)object;
|
||||
DefaultHttpResponse response = (DefaultHttpResponse) object;
|
||||
|
||||
switch(response.getStatus())
|
||||
{
|
||||
@@ -322,7 +322,7 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
else
|
||||
{
|
||||
mLog.error("Icecast HTTP broadcaster - unrecognized message [ " + object.getClass() +
|
||||
"] received:" + object.toString());
|
||||
"] received:" + object.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ package audio.broadcast.icecast;
|
||||
import audio.broadcast.BroadcastState;
|
||||
import audio.broadcast.icecast.codec.IcecastCodecFactory;
|
||||
import audio.convert.MP3AudioConverter;
|
||||
import controller.ThreadPoolManager;
|
||||
import org.apache.mina.core.RuntimeIoException;
|
||||
import org.apache.mina.core.future.ConnectFuture;
|
||||
import org.apache.mina.core.service.IoHandlerAdapter;
|
||||
@@ -31,6 +30,7 @@ import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import properties.SystemProperties;
|
||||
import util.ThreadPool;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
@@ -40,7 +40,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( IcecastTCPAudioBroadcaster.class );
|
||||
private final static Logger mLog = LoggerFactory.getLogger(IcecastTCPAudioBroadcaster.class);
|
||||
private final static String TERMINATOR = "\r\n";
|
||||
private final static String SEPARATOR = ":";
|
||||
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
|
||||
@@ -62,9 +62,9 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
*
|
||||
* @param configuration for the Icecast stream
|
||||
*/
|
||||
public IcecastTCPAudioBroadcaster(ThreadPoolManager threadPoolManager, IcecastTCPConfiguration configuration)
|
||||
public IcecastTCPAudioBroadcaster(IcecastTCPConfiguration configuration)
|
||||
{
|
||||
super(threadPoolManager, configuration);
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +90,7 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
private boolean connect()
|
||||
{
|
||||
if(!connected() && canConnect() &&
|
||||
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
|
||||
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
|
||||
mConnecting.compareAndSet(false, true))
|
||||
{
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
@@ -149,8 +149,7 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
}
|
||||
};
|
||||
|
||||
getThreadPoolManager().scheduleOnce(runnable, 0l, TimeUnit.SECONDS);
|
||||
|
||||
ThreadPool.SCHEDULED.schedule(runnable, 0l, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
return connected();
|
||||
@@ -241,9 +240,9 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
@Override
|
||||
public void exceptionCaught(IoSession session, Throwable cause) throws Exception
|
||||
{
|
||||
if(cause instanceof IOException && ((IOException)cause).getMessage().startsWith("Connection reset"))
|
||||
if(cause instanceof IOException && ((IOException) cause).getMessage().startsWith("Connection reset"))
|
||||
{
|
||||
IOException ioe = (IOException)cause;
|
||||
IOException ioe = (IOException) cause;
|
||||
|
||||
if(ioe.getMessage() != null && ioe.getMessage().startsWith("Connection reset"))
|
||||
{
|
||||
@@ -271,7 +270,7 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
{
|
||||
if(object instanceof String)
|
||||
{
|
||||
String message = (String)object;
|
||||
String message = (String) object;
|
||||
|
||||
if(message != null && !message.trim().isEmpty())
|
||||
{
|
||||
|
||||
@@ -21,7 +21,6 @@ package audio.broadcast.shoutcast.v1;
|
||||
import audio.broadcast.AudioBroadcaster;
|
||||
import audio.broadcast.BroadcastState;
|
||||
import audio.broadcast.IBroadcastMetadataUpdater;
|
||||
import controller.ThreadPoolManager;
|
||||
import org.apache.mina.core.RuntimeIoException;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.future.ConnectFuture;
|
||||
@@ -32,6 +31,7 @@ import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
|
||||
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import util.ThreadPool;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
@@ -41,7 +41,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( ShoutcastV1AudioBroadcaster.class );
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ShoutcastV1AudioBroadcaster.class);
|
||||
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
|
||||
|
||||
private NioSocketConnector mSocketConnector;
|
||||
@@ -61,9 +61,9 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
||||
*
|
||||
* @param configuration for the Shoutcast stream
|
||||
*/
|
||||
public ShoutcastV1AudioBroadcaster(ThreadPoolManager threadPoolManager, ShoutcastV1Configuration configuration)
|
||||
public ShoutcastV1AudioBroadcaster(ShoutcastV1Configuration configuration)
|
||||
{
|
||||
super(threadPoolManager, configuration);
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,7 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
||||
*/
|
||||
private ShoutcastV1Configuration getConfiguration()
|
||||
{
|
||||
return (ShoutcastV1Configuration)getBroadcastConfiguration();
|
||||
return (ShoutcastV1Configuration) getBroadcastConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,7 +79,7 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
||||
{
|
||||
if(mMetadataUpdater == null)
|
||||
{
|
||||
mMetadataUpdater = new ShoutcastV1BroadcastMetadataUpdater(getThreadPoolManager(), getConfiguration());
|
||||
mMetadataUpdater = new ShoutcastV1BroadcastMetadataUpdater(getConfiguration());
|
||||
}
|
||||
|
||||
return mMetadataUpdater;
|
||||
@@ -111,7 +111,7 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
||||
private boolean connect()
|
||||
{
|
||||
if(!connected() && canConnect() &&
|
||||
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
|
||||
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
|
||||
mConnecting.compareAndSet(false, true))
|
||||
{
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
@@ -171,8 +171,7 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
||||
}
|
||||
};
|
||||
|
||||
getThreadPoolManager().scheduleOnce(runnable, 0l, TimeUnit.SECONDS);
|
||||
|
||||
ThreadPool.SCHEDULED.schedule(runnable, 0l, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
return connected();
|
||||
@@ -242,9 +241,9 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
||||
@Override
|
||||
public void exceptionCaught(IoSession session, Throwable cause) throws Exception
|
||||
{
|
||||
if(cause instanceof IOException && ((IOException)cause).getMessage().startsWith("Connection reset"))
|
||||
if(cause instanceof IOException && ((IOException) cause).getMessage().startsWith("Connection reset"))
|
||||
{
|
||||
IOException ioe = (IOException)cause;
|
||||
IOException ioe = (IOException) cause;
|
||||
|
||||
if(ioe.getMessage() != null && ioe.getMessage().startsWith("Connection reset"))
|
||||
{
|
||||
@@ -272,7 +271,7 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
||||
{
|
||||
if(object instanceof String)
|
||||
{
|
||||
String message = (String)object;
|
||||
String message = (String) object;
|
||||
|
||||
if(message != null && !message.trim().isEmpty())
|
||||
{
|
||||
|
||||
@@ -20,7 +20,6 @@ package audio.broadcast.shoutcast.v1;
|
||||
|
||||
import audio.broadcast.IBroadcastMetadataUpdater;
|
||||
import channel.metadata.Metadata;
|
||||
import controller.ThreadPoolManager;
|
||||
import org.apache.mina.core.future.ConnectFuture;
|
||||
import org.apache.mina.core.service.IoHandlerAdapter;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
@@ -32,6 +31,7 @@ import org.apache.mina.http.api.HttpVersion;
|
||||
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import util.ThreadPool;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.ConnectException;
|
||||
@@ -49,7 +49,6 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ShoutcastV1BroadcastMetadataUpdater.class);
|
||||
private final static String UTF8 = "UTF-8";
|
||||
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
private ShoutcastV1Configuration mShoutcastV1Configuration;
|
||||
|
||||
private NioSocketConnector mSocketConnector;
|
||||
@@ -63,9 +62,8 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
|
||||
* broadcaster. When multiple metadata updates are received prior to completion of the current ongoing update
|
||||
* sequence, those updates will be queued and processed in the order received.
|
||||
*/
|
||||
public ShoutcastV1BroadcastMetadataUpdater(ThreadPoolManager threadPoolManager, ShoutcastV1Configuration shoutcastV1Configuration)
|
||||
public ShoutcastV1BroadcastMetadataUpdater(ShoutcastV1Configuration shoutcastV1Configuration)
|
||||
{
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
mShoutcastV1Configuration = shoutcastV1Configuration;
|
||||
}
|
||||
|
||||
@@ -127,7 +125,7 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
|
||||
|
||||
if(updateRequest != null)
|
||||
{
|
||||
mThreadPoolManager.scheduleOnce(new Runnable()
|
||||
ThreadPool.SCHEDULED.schedule(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
|
||||
@@ -38,7 +38,6 @@ import audio.broadcast.shoutcast.v2.ultravox.UltravoxMessageType;
|
||||
import audio.broadcast.shoutcast.v2.ultravox.UltravoxMetadata;
|
||||
import audio.broadcast.shoutcast.v2.ultravox.UltravoxProtocolFactory;
|
||||
import channel.metadata.Metadata;
|
||||
import controller.ThreadPoolManager;
|
||||
import org.apache.mina.core.RuntimeIoException;
|
||||
import org.apache.mina.core.future.ConnectFuture;
|
||||
import org.apache.mina.core.service.IoHandlerAdapter;
|
||||
@@ -48,6 +47,7 @@ import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import properties.SystemProperties;
|
||||
import util.ThreadPool;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
@@ -81,12 +81,11 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
||||
* This broadcaster uses the Apache Mina library for the streaming socket connection. The
|
||||
* ShoutcastV2IOHandler manages all interaction with the Shoutcast server and manages the overall broadcast state.
|
||||
*
|
||||
* @param threadPoolManager for thread pool access
|
||||
* @param configuration for the Shoutcast V2 stream
|
||||
*/
|
||||
public ShoutcastV2AudioBroadcaster(ThreadPoolManager threadPoolManager, ShoutcastV2Configuration configuration)
|
||||
public ShoutcastV2AudioBroadcaster(ShoutcastV2Configuration configuration)
|
||||
{
|
||||
super(threadPoolManager, configuration);
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
public ShoutcastV2Configuration getConfiguration()
|
||||
@@ -207,8 +206,7 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
||||
}
|
||||
};
|
||||
|
||||
getThreadPoolManager().scheduleOnce(runnable, 0l, TimeUnit.SECONDS);
|
||||
|
||||
ThreadPool.SCHEDULED.schedule(runnable, 0l, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
return connected();
|
||||
|
||||
@@ -22,13 +22,12 @@ import audio.AudioEvent;
|
||||
import audio.AudioPacket;
|
||||
import audio.AudioPacket.Type;
|
||||
import channel.metadata.Metadata;
|
||||
import controller.ThreadPoolManager;
|
||||
import controller.ThreadPoolManager.ThreadType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import source.mixer.MixerChannel;
|
||||
import util.ThreadPool;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.BooleanControl;
|
||||
@@ -59,7 +58,6 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
||||
private Listener<Metadata> mMetadataListener;
|
||||
private Broadcaster<AudioEvent> mAudioEventBroadcaster = new Broadcaster<>();
|
||||
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
private ScheduledFuture<?> mProcessorTask;
|
||||
|
||||
private SourceDataLine mOutput;
|
||||
@@ -82,21 +80,18 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
||||
* Maintains an internal non-blocking audio packet queue and processes this
|
||||
* queue 25 times a second (every 40 ms).
|
||||
*
|
||||
* @param threadPoolManager for assigning buffer processing schedule task
|
||||
* @param mixer to obtain source data line
|
||||
* @param mixerChannel either mono or left/right stereo
|
||||
* @param audioFormat to use during playback
|
||||
* @param lineInfo to use when obtaining the source data line
|
||||
* @param requestedBufferSize of approximately 1 second of audio
|
||||
*/
|
||||
public AudioOutput(ThreadPoolManager threadPoolManager,
|
||||
Mixer mixer,
|
||||
public AudioOutput(Mixer mixer,
|
||||
MixerChannel mixerChannel,
|
||||
AudioFormat audioFormat,
|
||||
Line.Info lineInfo,
|
||||
int requestedBufferSize)
|
||||
{
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
mMixer = mixer;
|
||||
mMixerChannel = mixerChannel;
|
||||
|
||||
@@ -141,9 +136,8 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
||||
}
|
||||
|
||||
/* Run the queue processor task every 40 milliseconds or 25 times a second */
|
||||
mProcessorTask = mThreadPoolManager.scheduleFixedRate(
|
||||
ThreadType.AUDIO_PROCESSING, new BufferProcessor(),
|
||||
40, TimeUnit.MILLISECONDS);
|
||||
mProcessorTask = ThreadPool.SCHEDULED.scheduleAtFixedRate(new BufferProcessor(),
|
||||
0, 40, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
mAudioStartEvent = new AudioEvent(AudioEvent.Type.AUDIO_STARTED,
|
||||
@@ -170,12 +164,11 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
||||
{
|
||||
mCanProcessAudio = false;
|
||||
|
||||
if(mProcessorTask != null && mThreadPoolManager != null)
|
||||
if(mProcessorTask != null)
|
||||
{
|
||||
mThreadPoolManager.cancel(mProcessorTask);
|
||||
mProcessorTask.cancel(true);
|
||||
}
|
||||
|
||||
mThreadPoolManager = null;
|
||||
mProcessorTask = null;
|
||||
|
||||
mBuffer.clear();
|
||||
|
||||
@@ -1,74 +1,67 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.output;
|
||||
|
||||
import audio.AudioFormats;
|
||||
import audio.AudioPacket;
|
||||
import source.mixer.MixerChannel;
|
||||
|
||||
import javax.sound.sampled.Mixer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import javax.sound.sampled.Mixer;
|
||||
|
||||
import source.mixer.MixerChannel;
|
||||
import audio.AudioFormats;
|
||||
import audio.AudioPacket;
|
||||
import controller.ThreadPoolManager;
|
||||
|
||||
/**
|
||||
* Mono Audio output implementation
|
||||
*/
|
||||
public class MonoAudioOutput extends AudioOutput
|
||||
{
|
||||
private final static int BUFFER_SIZE = 8000;
|
||||
|
||||
public MonoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer )
|
||||
{
|
||||
super( threadPoolManager,
|
||||
mixer,
|
||||
MixerChannel.MONO,
|
||||
AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO,
|
||||
AudioFormats.MONO_SOURCE_DATALINE_INFO,
|
||||
BUFFER_SIZE );
|
||||
}
|
||||
private final static int BUFFER_SIZE = 8000;
|
||||
|
||||
public MonoAudioOutput(Mixer mixer)
|
||||
{
|
||||
super(mixer, MixerChannel.MONO, AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO,
|
||||
AudioFormats.MONO_SOURCE_DATALINE_INFO, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the audio packet data into mono audio frames.
|
||||
*/
|
||||
protected ByteBuffer convert(AudioPacket packet)
|
||||
{
|
||||
if(packet.hasAudioBuffer())
|
||||
{
|
||||
float[] samples = packet.getAudioBuffer().getSamples();
|
||||
|
||||
/**
|
||||
* Converts the audio packet data into mono audio frames.
|
||||
*/
|
||||
protected ByteBuffer convert( AudioPacket packet )
|
||||
{
|
||||
if( packet.hasAudioBuffer() )
|
||||
{
|
||||
float[] samples = packet.getAudioBuffer().getSamples();
|
||||
|
||||
/* Little-endian byte buffer */
|
||||
ByteBuffer buffer =
|
||||
ByteBuffer.allocate( samples.length * 2 )
|
||||
.order( ByteOrder.LITTLE_ENDIAN );
|
||||
ByteBuffer buffer = ByteBuffer.allocate(samples.length * 2).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
ShortBuffer shortBuffer = buffer.asShortBuffer();
|
||||
ShortBuffer shortBuffer = buffer.asShortBuffer();
|
||||
|
||||
for( float sample: samples )
|
||||
{
|
||||
shortBuffer.put( (short)( sample * Short.MAX_VALUE ) );
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
for(float sample : samples)
|
||||
{
|
||||
shortBuffer.put((short) (sample * Short.MAX_VALUE));
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,88 +1,82 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.output;
|
||||
|
||||
import audio.AudioFormats;
|
||||
import audio.AudioPacket;
|
||||
import source.mixer.MixerChannel;
|
||||
|
||||
import javax.sound.sampled.Mixer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import javax.sound.sampled.Mixer;
|
||||
|
||||
import source.mixer.MixerChannel;
|
||||
import audio.AudioFormats;
|
||||
import audio.AudioPacket;
|
||||
import controller.ThreadPoolManager;
|
||||
|
||||
/**
|
||||
* Stereo audio output implementation.
|
||||
* Stereo audio output implementation.
|
||||
*/
|
||||
public class StereoAudioOutput extends AudioOutput
|
||||
{
|
||||
private final static int BUFFER_SIZE = 16000;
|
||||
|
||||
public StereoAudioOutput( ThreadPoolManager threadPoolManager,
|
||||
Mixer mixer, MixerChannel channel )
|
||||
{
|
||||
super( threadPoolManager,
|
||||
mixer,
|
||||
channel,
|
||||
AudioFormats.PCM_SIGNED_8KHZ_16BITS_STEREO,
|
||||
AudioFormats.STEREO_SOURCE_DATALINE_INFO,
|
||||
BUFFER_SIZE );
|
||||
}
|
||||
private final static int BUFFER_SIZE = 16000;
|
||||
|
||||
public StereoAudioOutput(Mixer mixer, MixerChannel channel)
|
||||
{
|
||||
super(mixer, channel, AudioFormats.PCM_SIGNED_8KHZ_16BITS_STEREO, AudioFormats.STEREO_SOURCE_DATALINE_INFO,
|
||||
BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the audio packet data into stereo audio frames with the mixer
|
||||
* channel containing the audio and the other channel containing zero
|
||||
* valued (silent) samples.
|
||||
*/
|
||||
protected ByteBuffer convert(AudioPacket packet)
|
||||
{
|
||||
if(packet.hasAudioBuffer())
|
||||
{
|
||||
float[] samples = packet.getAudioBuffer().getSamples();
|
||||
|
||||
/**
|
||||
* Converts the audio packet data into stereo audio frames with the mixer
|
||||
* channel containing the audio and the other channel containing zero
|
||||
* valued (silent) samples.
|
||||
*/
|
||||
protected ByteBuffer convert( AudioPacket packet )
|
||||
{
|
||||
if( packet.hasAudioBuffer() )
|
||||
{
|
||||
float[] samples = packet.getAudioBuffer().getSamples();
|
||||
|
||||
/* Little-endian byte buffer */
|
||||
ByteBuffer buffer = ByteBuffer.allocate( samples.length * 4 )
|
||||
.order( ByteOrder.LITTLE_ENDIAN );
|
||||
|
||||
ShortBuffer shortBuffer = buffer.asShortBuffer();
|
||||
ByteBuffer buffer = ByteBuffer.allocate(samples.length * 4)
|
||||
.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
if( getMixerChannel() == MixerChannel.LEFT )
|
||||
{
|
||||
for( float sample: samples )
|
||||
{
|
||||
shortBuffer.put( (short)( sample * Short.MAX_VALUE ) );
|
||||
shortBuffer.put( (short)0 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( float sample: samples )
|
||||
{
|
||||
shortBuffer.put( (short)0 );
|
||||
shortBuffer.put( (short)( sample * Short.MAX_VALUE ) );
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
ShortBuffer shortBuffer = buffer.asShortBuffer();
|
||||
|
||||
if(getMixerChannel() == MixerChannel.LEFT)
|
||||
{
|
||||
for(float sample : samples)
|
||||
{
|
||||
shortBuffer.put((short) (sample * Short.MAX_VALUE));
|
||||
shortBuffer.put((short) 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(float sample : samples)
|
||||
{
|
||||
shortBuffer.put((short) 0);
|
||||
shortBuffer.put((short) (sample * Short.MAX_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ package channel.metadata;
|
||||
|
||||
import alias.Alias;
|
||||
import channel.state.State;
|
||||
import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory;
|
||||
import controller.channel.Channel;
|
||||
import controller.channel.ChannelProcessingManager;
|
||||
import icon.IconManager;
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ThreadPoolManager
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( ThreadPoolManager.class );
|
||||
|
||||
private int THREAD_POOL_SIZE = 2;
|
||||
|
||||
private HashMap<ScheduledFuture<?>,ThreadType> mTasks =
|
||||
new HashMap<ScheduledFuture<?>,ThreadType>();
|
||||
|
||||
private ScheduledExecutorService mExecutor;
|
||||
|
||||
public ThreadPoolManager()
|
||||
{
|
||||
}
|
||||
|
||||
public ScheduledExecutorService getScheduledExecutorService()
|
||||
{
|
||||
if( mExecutor == null )
|
||||
{
|
||||
mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE,
|
||||
new NamingThreadFactory( "sdrtrunk" ) );
|
||||
}
|
||||
|
||||
return mExecutor;
|
||||
}
|
||||
|
||||
public ScheduledFuture<?> scheduleFixedRate( ThreadType type,
|
||||
Runnable command,
|
||||
long period,
|
||||
TimeUnit unit )
|
||||
throws RejectedExecutionException
|
||||
{
|
||||
ScheduledExecutorService executorService = getScheduledExecutorService();
|
||||
|
||||
ScheduledFuture<?> task =
|
||||
executorService.scheduleAtFixedRate( command, 0, period, unit );
|
||||
|
||||
mTasks.put( task, type );
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
public void scheduleOnce( Runnable command, long delay, TimeUnit unit )
|
||||
throws RejectedExecutionException
|
||||
{
|
||||
ScheduledExecutorService executorService = getScheduledExecutorService();
|
||||
|
||||
executorService.schedule( command, delay, unit );
|
||||
}
|
||||
|
||||
public boolean cancel( ScheduledFuture<?> task )
|
||||
{
|
||||
boolean success = task.cancel( true );
|
||||
|
||||
mTasks.remove( task );
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public int getTaskCount( ThreadType type )
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for( ThreadType current: mTasks.values() )
|
||||
{
|
||||
if( current == type )
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public enum ThreadType
|
||||
{
|
||||
AUDIO_PROCESSING,
|
||||
AUDIO_RECORDING,
|
||||
BASEBAND_RECORDING,
|
||||
SOURCE_SAMPLE_PROCESSING,
|
||||
DECIMATION,
|
||||
DECODER;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,20 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package gui;
|
||||
|
||||
@@ -24,9 +25,6 @@ import audio.broadcast.BroadcastModel;
|
||||
import audio.broadcast.BroadcastStatusPanel;
|
||||
import com.jidesoft.swing.JideSplitPane;
|
||||
import controller.ControllerPanel;
|
||||
import controller.ThreadPoolManager;
|
||||
import controller.ThreadPoolManager.ThreadType;
|
||||
import controller.channel.ChannelEventListener;
|
||||
import controller.channel.ChannelModel;
|
||||
import controller.channel.ChannelProcessingManager;
|
||||
import controller.channel.ChannelSelectionManager;
|
||||
@@ -48,6 +46,7 @@ import source.tuner.TunerModel;
|
||||
import source.tuner.TunerSpectralDisplayManager;
|
||||
import source.tuner.configuration.TunerConfigurationModel;
|
||||
import spectrum.SpectralDisplayPanel;
|
||||
import util.ThreadPool;
|
||||
import util.TimeStamp;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
@@ -61,7 +60,6 @@ import java.io.IOException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class SDRTrunk implements Listener<TunerEvent>
|
||||
{
|
||||
@@ -80,26 +78,28 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
|
||||
private String mTitle;
|
||||
|
||||
private boolean mLogChannelAndMemoryUsage = false;
|
||||
|
||||
public SDRTrunk()
|
||||
{
|
||||
mLog.info("");
|
||||
mLog.info("");
|
||||
mLog.info("*******************************************************************");
|
||||
mLog.info("**** sdrtrunk: a trunked radio and digital decoding application ***");
|
||||
mLog.info("**** website: https://github.com/dsheirer/sdrtrunk ***");
|
||||
mLog.info("*******************************************************************");
|
||||
mLog.info("");
|
||||
mLog.info("");
|
||||
mLog.info("Host CPU Cores: " + Runtime.getRuntime().availableProcessors());
|
||||
mLog.info("Host Memory Total: " + Runtime.getRuntime().totalMemory());
|
||||
mLog.info("Host Memory Max: " + Runtime.getRuntime().maxMemory());
|
||||
mLog.info("Host Memory Free: " + Runtime.getRuntime().freeMemory());
|
||||
|
||||
//Setup the application home directory
|
||||
Path home = getHomePath();
|
||||
|
||||
ThreadPool.logSettings();
|
||||
|
||||
mLog.info("Home path: " + home.toString());
|
||||
|
||||
//Load properties file
|
||||
if (home != null)
|
||||
if(home != null)
|
||||
{
|
||||
loadProperties(home);
|
||||
}
|
||||
@@ -110,11 +110,9 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
TunerConfigurationModel tunerConfigurationModel = new TunerConfigurationModel();
|
||||
TunerModel tunerModel = new TunerModel(tunerConfigurationModel);
|
||||
|
||||
ThreadPoolManager threadPoolManager = new ThreadPoolManager();
|
||||
mIconManager = new IconManager();
|
||||
|
||||
mIconManager = new IconManager(threadPoolManager);
|
||||
|
||||
mSettingsManager = new SettingsManager(threadPoolManager, tunerConfigurationModel);
|
||||
mSettingsManager = new SettingsManager(tunerConfigurationModel);
|
||||
|
||||
AliasModel aliasModel = new AliasModel();
|
||||
|
||||
@@ -124,28 +122,27 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
|
||||
EventLogManager eventLogManager = new EventLogManager();
|
||||
|
||||
RecorderManager recorderManager = new RecorderManager(threadPoolManager);
|
||||
RecorderManager recorderManager = new RecorderManager();
|
||||
|
||||
SourceManager sourceManager = new SourceManager(tunerModel,
|
||||
mSettingsManager, threadPoolManager);
|
||||
SourceManager sourceManager = new SourceManager(tunerModel, mSettingsManager);
|
||||
|
||||
ChannelProcessingManager channelProcessingManager = new ChannelProcessingManager(
|
||||
channelModel, channelMapModel, aliasModel, eventLogManager, recorderManager, sourceManager);
|
||||
channelModel, channelMapModel, aliasModel, eventLogManager, recorderManager, sourceManager);
|
||||
channelProcessingManager.addAudioPacketListener(recorderManager);
|
||||
|
||||
channelModel.addListener(channelProcessingManager);
|
||||
|
||||
ChannelSelectionManager channelSelectionManager =
|
||||
new ChannelSelectionManager(channelModel);
|
||||
new ChannelSelectionManager(channelModel);
|
||||
channelModel.addListener(channelSelectionManager);
|
||||
|
||||
AliasActionManager aliasActionManager = new AliasActionManager(threadPoolManager);
|
||||
AliasActionManager aliasActionManager = new AliasActionManager();
|
||||
channelProcessingManager.addMessageListener(aliasActionManager);
|
||||
|
||||
AudioManager audioManager = new AudioManager(threadPoolManager, sourceManager.getMixerManager());
|
||||
AudioManager audioManager = new AudioManager(sourceManager.getMixerManager());
|
||||
channelProcessingManager.addAudioPacketListener(audioManager);
|
||||
|
||||
mBroadcastModel = new BroadcastModel(threadPoolManager, mIconManager);
|
||||
mBroadcastModel = new BroadcastModel(mIconManager);
|
||||
|
||||
channelProcessingManager.addAudioPacketListener(mBroadcastModel);
|
||||
|
||||
@@ -153,31 +150,23 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
channelProcessingManager.addMessageListener(mapService);
|
||||
|
||||
mControllerPanel = new ControllerPanel(audioManager, aliasModel, mBroadcastModel,
|
||||
channelModel, channelMapModel, channelProcessingManager, mIconManager,
|
||||
mapService, mSettingsManager, sourceManager, tunerModel);
|
||||
channelModel, channelMapModel, channelProcessingManager, mIconManager,
|
||||
mapService, mSettingsManager, sourceManager, tunerModel);
|
||||
|
||||
mSpectralPanel = new SpectralDisplayPanel(channelModel,
|
||||
channelProcessingManager, mSettingsManager);
|
||||
channelProcessingManager, mSettingsManager);
|
||||
|
||||
TunerSpectralDisplayManager tunerSpectralDisplayManager =
|
||||
new TunerSpectralDisplayManager(mSpectralPanel,
|
||||
channelModel, channelProcessingManager, mSettingsManager);
|
||||
new TunerSpectralDisplayManager(mSpectralPanel,
|
||||
channelModel, channelProcessingManager, mSettingsManager);
|
||||
tunerModel.addListener(tunerSpectralDisplayManager);
|
||||
tunerModel.addListener(this);
|
||||
|
||||
PlaylistManager playlistManager = new PlaylistManager(threadPoolManager,
|
||||
aliasModel, mBroadcastModel, channelModel, channelMapModel);
|
||||
PlaylistManager playlistManager = new PlaylistManager(aliasModel, mBroadcastModel, channelModel,
|
||||
channelMapModel);
|
||||
|
||||
playlistManager.init();
|
||||
|
||||
if (mLogChannelAndMemoryUsage)
|
||||
{
|
||||
Runnable cml = new ChannelMemoryLogger();
|
||||
channelModel.addListener((ChannelEventListener) cml);
|
||||
threadPoolManager.scheduleFixedRate(
|
||||
ThreadType.DECODER, cml, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
mLog.info("starting main application gui");
|
||||
|
||||
//Initialize the GUI
|
||||
@@ -194,7 +183,7 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
{
|
||||
mMainGui.setVisible(true);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -263,15 +252,15 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
{
|
||||
Desktop.getDesktop().open(getHomePath().toFile());
|
||||
}
|
||||
catch (Exception e)
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Couldn't open file explorer");
|
||||
|
||||
JOptionPane.showMessageDialog(mMainGui,
|
||||
"Can't launch file explorer - files are located at: " +
|
||||
getHomePath().toString(),
|
||||
"Can't launch file explorer",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
"Can't launch file explorer - files are located at: " +
|
||||
getHomePath().toString(),
|
||||
"Can't launch file explorer",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -292,13 +281,13 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
|
||||
JMenuItem exitMenu = new JMenuItem("Exit");
|
||||
exitMenu.addActionListener(
|
||||
new ActionListener()
|
||||
new ActionListener()
|
||||
{
|
||||
public void actionPerformed(ActionEvent event)
|
||||
{
|
||||
public void actionPerformed(ActionEvent event)
|
||||
{
|
||||
System.exit(0);
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
fileMenu.add(exitMenu);
|
||||
@@ -313,7 +302,7 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
|
||||
screenCaptureItem.setMnemonic(KeyEvent.VK_C);
|
||||
screenCaptureItem.setAccelerator(
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.ALT_MASK));
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.ALT_MASK));
|
||||
|
||||
screenCaptureItem.addActionListener(new ActionListener()
|
||||
{
|
||||
@@ -325,28 +314,28 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
Robot robot = new Robot();
|
||||
|
||||
final BufferedImage image =
|
||||
robot.createScreenCapture(mMainGui.getBounds());
|
||||
robot.createScreenCapture(mMainGui.getBounds());
|
||||
|
||||
SystemProperties props = SystemProperties.getInstance();
|
||||
|
||||
Path capturePath = props.getApplicationFolder("screen_captures");
|
||||
|
||||
if (!Files.exists(capturePath))
|
||||
if(!Files.exists(capturePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
Files.createDirectory(capturePath);
|
||||
}
|
||||
catch (IOException e)
|
||||
catch(IOException e)
|
||||
{
|
||||
mLog.error("Couldn't create 'screen_captures' "
|
||||
+ "subdirectory in the " +
|
||||
"SDRTrunk application directory", e);
|
||||
+ "subdirectory in the " +
|
||||
"SDRTrunk application directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
String filename = TimeStamp.getTimeStamp("_") +
|
||||
"_screen_capture.png";
|
||||
"_screen_capture.png";
|
||||
|
||||
final Path captureFile = capturePath.resolve(filename);
|
||||
|
||||
@@ -358,17 +347,17 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
try
|
||||
{
|
||||
ImageIO.write(image, "png",
|
||||
captureFile.toFile());
|
||||
captureFile.toFile());
|
||||
}
|
||||
catch (IOException e)
|
||||
catch(IOException e)
|
||||
{
|
||||
mLog.error("Couldn't write screen capture to "
|
||||
+ "file [" + captureFile.toString() + "]", e);
|
||||
+ "file [" + captureFile.toString() + "]", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (AWTException e)
|
||||
catch(AWTException e)
|
||||
{
|
||||
mLog.error("Exception while taking screen capture", e);
|
||||
}
|
||||
@@ -430,30 +419,30 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
{
|
||||
Path propsPath = homePath.resolve("SDRTrunk.properties");
|
||||
|
||||
if (!Files.exists(propsPath))
|
||||
if(!Files.exists(propsPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
mLog.info("SDRTrunk - creating application properties file [" +
|
||||
propsPath.toAbsolutePath() + "]");
|
||||
propsPath.toAbsolutePath() + "]");
|
||||
|
||||
Files.createFile(propsPath);
|
||||
}
|
||||
catch (IOException e)
|
||||
catch(IOException e)
|
||||
{
|
||||
mLog.error("SDRTrunk - couldn't create application properties "
|
||||
+ "file [" + propsPath.toAbsolutePath(), e);
|
||||
+ "file [" + propsPath.toAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (Files.exists(propsPath))
|
||||
if(Files.exists(propsPath))
|
||||
{
|
||||
SystemProperties.getInstance().load(propsPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("SDRTrunk - couldn't find or recreate the SDRTrunk " +
|
||||
"application properties file");
|
||||
"application properties file");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,23 +455,23 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
private Path getHomePath()
|
||||
{
|
||||
Path homePath = FileSystems.getDefault()
|
||||
.getPath(System.getProperty("user.home"), "SDRTrunk");
|
||||
.getPath(System.getProperty("user.home"), "SDRTrunk");
|
||||
|
||||
if (!Files.exists(homePath))
|
||||
if(!Files.exists(homePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
Files.createDirectory(homePath);
|
||||
|
||||
mLog.info("SDRTrunk - created application home directory [" +
|
||||
homePath.toString() + "]");
|
||||
homePath.toString() + "]");
|
||||
}
|
||||
catch (Exception e)
|
||||
catch(Exception e)
|
||||
{
|
||||
homePath = null;
|
||||
|
||||
mLog.error("SDRTrunk: exception while creating SDRTrunk home " +
|
||||
"directory in the user's home directory", e);
|
||||
"directory in the user's home directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +481,7 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
@Override
|
||||
public void receive(TunerEvent event)
|
||||
{
|
||||
if (event.getEvent() == TunerEvent.Event.REQUEST_MAIN_SPECTRAL_DISPLAY)
|
||||
if(event.getEvent() == TunerEvent.Event.REQUEST_MAIN_SPECTRAL_DISPLAY)
|
||||
{
|
||||
mMainGui.setTitle(mTitle + " - " + event.getTuner().getName());
|
||||
}
|
||||
|
||||
@@ -18,14 +18,12 @@
|
||||
******************************************************************************/
|
||||
package icon;
|
||||
|
||||
import controller.ThreadPoolManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import properties.SystemProperties;
|
||||
import util.ThreadPool;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListDataEvent;
|
||||
import javax.swing.event.ListDataListener;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.event.TableModelListener;
|
||||
import javax.xml.bind.JAXBContext;
|
||||
@@ -54,16 +52,14 @@ public class IconManager
|
||||
private Path mIconBackupFilePath;
|
||||
private Path mIconLockFilePath;
|
||||
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
private IconEditor mIconEditor;
|
||||
private IconTableModel mIconTableModel;
|
||||
private AtomicBoolean mSavingIcons = new AtomicBoolean();
|
||||
|
||||
private Map<String, ImageIcon> mResizedIcons = new HashMap<>();
|
||||
private Map<String,ImageIcon> mResizedIcons = new HashMap<>();
|
||||
|
||||
public IconManager(ThreadPoolManager threadPoolManager)
|
||||
public IconManager()
|
||||
{
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,7 +408,7 @@ public class IconManager
|
||||
{
|
||||
if(mSavingIcons.compareAndSet(false, true))
|
||||
{
|
||||
mThreadPoolManager.scheduleOnce(new IconSaveTask(), 2, TimeUnit.SECONDS);
|
||||
ThreadPool.SCHEDULED.schedule(new IconSaveTask(), 2, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,83 +1,77 @@
|
||||
package instrument.gui;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDesktopPane;
|
||||
import javax.swing.JInternalFrame;
|
||||
|
||||
import alias.AliasModel;
|
||||
import audio.broadcast.BroadcastModel;
|
||||
import controller.channel.ChannelModel;
|
||||
import controller.channel.map.ChannelMapModel;
|
||||
import module.decode.DecodeConfigurationEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import playlist.PlaylistManager;
|
||||
import source.IControllableFileSource;
|
||||
import alias.AliasModel;
|
||||
import controller.ThreadPoolManager;
|
||||
import controller.channel.ChannelModel;
|
||||
import controller.channel.map.ChannelMapModel;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class DecoderSelectionFrame extends JInternalFrame
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private DecodeConfigurationEditor mDecodeEditor = new DecodeConfigurationEditor( null );
|
||||
private PlaylistManager mPlaylistManager;
|
||||
|
||||
private IControllableFileSource mSource;
|
||||
private JDesktopPane mDesktop;
|
||||
|
||||
public DecoderSelectionFrame( JDesktopPane desktop,
|
||||
IControllableFileSource source )
|
||||
{
|
||||
ThreadPoolManager tpm = new ThreadPoolManager();
|
||||
private DecodeConfigurationEditor mDecodeEditor = new DecodeConfigurationEditor(null);
|
||||
private PlaylistManager mPlaylistManager;
|
||||
|
||||
AliasModel aliasModel = new AliasModel();
|
||||
BroadcastModel broadcastModel = new BroadcastModel(tpm, null);
|
||||
ChannelModel channelModel = new ChannelModel();
|
||||
ChannelMapModel channelMapModel = new ChannelMapModel();
|
||||
private IControllableFileSource mSource;
|
||||
private JDesktopPane mDesktop;
|
||||
|
||||
mPlaylistManager = new PlaylistManager( tpm, aliasModel, broadcastModel, channelModel, channelMapModel );
|
||||
public DecoderSelectionFrame(JDesktopPane desktop,
|
||||
IControllableFileSource source)
|
||||
{
|
||||
AliasModel aliasModel = new AliasModel();
|
||||
BroadcastModel broadcastModel = new BroadcastModel(null);
|
||||
ChannelModel channelModel = new ChannelModel();
|
||||
ChannelMapModel channelMapModel = new ChannelMapModel();
|
||||
|
||||
mDesktop = desktop;
|
||||
mSource = source;
|
||||
|
||||
initGUI();
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "", "[grow,fill]", "[][][grow,fill]" ) );
|
||||
mPlaylistManager = new PlaylistManager(aliasModel, broadcastModel, channelModel, channelMapModel);
|
||||
|
||||
setTitle( "Decoders" );
|
||||
setPreferredSize( new Dimension( 700, 450 ) );
|
||||
setSize( 700, 450 );
|
||||
mDesktop = desktop;
|
||||
mSource = source;
|
||||
|
||||
setResizable( true );
|
||||
setClosable( true );
|
||||
setIconifiable( true );
|
||||
setMaximizable( false );
|
||||
initGUI();
|
||||
}
|
||||
|
||||
add( mDecodeEditor, "wrap" );
|
||||
|
||||
add( new AddDecoderButton(), "span" );
|
||||
|
||||
}
|
||||
|
||||
public class AddDecoderButton extends JButton
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout(new MigLayout("", "[grow,fill]", "[][][grow,fill]"));
|
||||
|
||||
public AddDecoderButton()
|
||||
{
|
||||
super( "Add" );
|
||||
|
||||
addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent arg0 )
|
||||
{
|
||||
setTitle("Decoders");
|
||||
setPreferredSize(new Dimension(700, 450));
|
||||
setSize(700, 450);
|
||||
|
||||
setResizable(true);
|
||||
setClosable(true);
|
||||
setIconifiable(true);
|
||||
setMaximizable(false);
|
||||
|
||||
add(mDecodeEditor, "wrap");
|
||||
|
||||
add(new AddDecoderButton(), "span");
|
||||
|
||||
}
|
||||
|
||||
public class AddDecoderButton extends JButton
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public AddDecoderButton()
|
||||
{
|
||||
super("Add");
|
||||
|
||||
addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0)
|
||||
{
|
||||
// DecodeConfiguration config = mDecodeEditor.getDecodeConfig();
|
||||
//
|
||||
// if( config != null )
|
||||
@@ -89,9 +83,9 @@ public class DecoderSelectionFrame extends JInternalFrame
|
||||
//
|
||||
// mDesktop.add( decoderFrame );
|
||||
// }
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
package map;
|
||||
|
||||
import alias.Alias;
|
||||
import controller.NamingThreadFactory;
|
||||
import icon.IconManager;
|
||||
import message.Message;
|
||||
import sample.Listener;
|
||||
@@ -28,9 +27,8 @@ import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
public class MapService implements Listener<Message>
|
||||
{
|
||||
@@ -39,12 +37,7 @@ public class MapService implements Listener<Message>
|
||||
|
||||
private static final Color sDEFAULT_COLOR = Color.BLACK;
|
||||
|
||||
private final ScheduledExecutorService scheduler =
|
||||
Executors.newScheduledThreadPool(1,
|
||||
new NamingThreadFactory("map tile fetcher"));
|
||||
|
||||
private ArrayList<PlottableUpdateListener> mListeners =
|
||||
new ArrayList<PlottableUpdateListener>();
|
||||
private List<PlottableUpdateListener> mListeners = new ArrayList<PlottableUpdateListener>();
|
||||
|
||||
private HashMap<String, PlottableEntity> mEntities =
|
||||
new HashMap<String, PlottableEntity>();
|
||||
|
||||
@@ -34,7 +34,6 @@ import channel.state.DecoderStateEvent.Event;
|
||||
import channel.state.IDecoderStateEventListener;
|
||||
import channel.state.IDecoderStateEventProvider;
|
||||
import channel.state.State;
|
||||
import controller.NamingThreadFactory;
|
||||
import controller.channel.Channel.ChannelType;
|
||||
import controller.channel.ChannelEvent;
|
||||
import controller.channel.IChannelEventListener;
|
||||
@@ -69,11 +68,10 @@ import source.tuner.TunerChannelSource;
|
||||
import source.tuner.frequency.FrequencyChangeEvent;
|
||||
import source.tuner.frequency.IFrequencyChangeListener;
|
||||
import source.tuner.frequency.IFrequencyChangeProvider;
|
||||
import util.ThreadPool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
@@ -110,7 +108,6 @@ public class ProcessingChain implements IChannelEventListener
|
||||
private Broadcaster<RealBuffer> mUnFilteredRealBufferBroadcaster = new Broadcaster<>();
|
||||
private Broadcaster<SquelchState> mSquelchStateBroadcaster = new Broadcaster<>();
|
||||
|
||||
private ScheduledExecutorService mScheduledExecutorService;
|
||||
private String mName;
|
||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||
|
||||
@@ -170,9 +167,6 @@ public class ProcessingChain implements IChannelEventListener
|
||||
|
||||
mModules.clear();
|
||||
|
||||
mScheduledExecutorService.shutdownNow();
|
||||
mScheduledExecutorService = null;
|
||||
|
||||
mAudioPacketBroadcaster.dispose();
|
||||
mCallEventBroadcaster.dispose();
|
||||
mChannelEventBroadcaster.dispose();
|
||||
@@ -180,11 +174,6 @@ public class ProcessingChain implements IChannelEventListener
|
||||
mMessageBroadcaster.dispose();
|
||||
mFilteredRealBufferBroadcaster.dispose();
|
||||
mSquelchStateBroadcaster.dispose();
|
||||
|
||||
if(mScheduledExecutorService != null)
|
||||
{
|
||||
mScheduledExecutorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -584,18 +573,12 @@ public class ProcessingChain implements IChannelEventListener
|
||||
}
|
||||
}
|
||||
|
||||
if(mScheduledExecutorService == null)
|
||||
{
|
||||
mScheduledExecutorService = Executors.newScheduledThreadPool(
|
||||
1, new NamingThreadFactory("channel " + mName));
|
||||
}
|
||||
|
||||
/* Start each of the modules */
|
||||
for(Module module : mModules)
|
||||
{
|
||||
try
|
||||
{
|
||||
module.start(mScheduledExecutorService);
|
||||
module.start(ThreadPool.SCHEDULED);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
@@ -603,7 +586,7 @@ public class ProcessingChain implements IChannelEventListener
|
||||
}
|
||||
}
|
||||
|
||||
mSource.start(mScheduledExecutorService);
|
||||
mSource.start(ThreadPool.SCHEDULED);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -21,7 +21,6 @@ import alias.AliasEvent;
|
||||
import alias.AliasModel;
|
||||
import audio.broadcast.BroadcastEvent;
|
||||
import audio.broadcast.BroadcastModel;
|
||||
import controller.ThreadPoolManager;
|
||||
import controller.channel.Channel.ChannelType;
|
||||
import controller.channel.ChannelEvent;
|
||||
import controller.channel.ChannelEventListener;
|
||||
@@ -33,6 +32,7 @@ import org.slf4j.LoggerFactory;
|
||||
import playlist.version1.PlaylistConverterV1ToV2;
|
||||
import properties.SystemProperties;
|
||||
import sample.Listener;
|
||||
import util.ThreadPool;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
@@ -51,7 +51,6 @@ public class PlaylistManager implements ChannelEventListener
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(PlaylistManager.class);
|
||||
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
private AliasModel mAliasModel;
|
||||
private BroadcastModel mBroadcastModel;
|
||||
private ChannelModel mChannelModel;
|
||||
@@ -72,16 +71,10 @@ public class PlaylistManager implements ChannelEventListener
|
||||
* Monitors playlist changes to automatically save configuration changes
|
||||
* after they occur.
|
||||
*
|
||||
* @param threadPoolManager
|
||||
* @param channelModel
|
||||
*/
|
||||
public PlaylistManager(ThreadPoolManager threadPoolManager,
|
||||
AliasModel aliasModel,
|
||||
BroadcastModel broadcastModel,
|
||||
ChannelModel channelModel,
|
||||
ChannelMapModel channelMapModel)
|
||||
public PlaylistManager(AliasModel aliasModel, BroadcastModel broadcastModel, ChannelModel channelModel, ChannelMapModel channelMapModel)
|
||||
{
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
mAliasModel = aliasModel;
|
||||
mBroadcastModel = broadcastModel;
|
||||
mChannelModel = channelModel;
|
||||
@@ -421,7 +414,7 @@ public class PlaylistManager implements ChannelEventListener
|
||||
{
|
||||
if(mPlaylistSavePending.compareAndSet(false, true))
|
||||
{
|
||||
mThreadPoolManager.scheduleOnce(new PlaylistSaveTask(), 2, TimeUnit.SECONDS);
|
||||
ThreadPool.SCHEDULED.schedule(new PlaylistSaveTask(), 2, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,11 @@ package properties;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import util.ThreadPool;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -34,8 +34,6 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@@ -54,7 +52,6 @@ public class SystemProperties
|
||||
private Path mPropertiesPath;
|
||||
private String mApplicationName;
|
||||
private AtomicBoolean mSavePending = new AtomicBoolean();
|
||||
private ScheduledExecutorService mScheduledExecutorService;
|
||||
|
||||
private SystemProperties()
|
||||
{
|
||||
@@ -83,12 +80,7 @@ public class SystemProperties
|
||||
{
|
||||
if(mSavePending.compareAndSet(false, true))
|
||||
{
|
||||
if(mScheduledExecutorService == null)
|
||||
{
|
||||
mScheduledExecutorService = Executors.newScheduledThreadPool(1);
|
||||
}
|
||||
|
||||
mScheduledExecutorService.schedule(new SavePropertiesTask(), 2, TimeUnit.SECONDS);
|
||||
ThreadPool.SCHEDULED.schedule(new SavePropertiesTask(), 2, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,13 +19,13 @@ package record;
|
||||
|
||||
import audio.AudioPacket;
|
||||
import channel.metadata.Metadata;
|
||||
import controller.ThreadPoolManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import properties.SystemProperties;
|
||||
import record.wave.ComplexBufferWaveRecorder;
|
||||
import record.wave.RealBufferWaveRecorder;
|
||||
import sample.Listener;
|
||||
import util.ThreadPool;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
@@ -39,22 +39,18 @@ public class RecorderManager implements Listener<AudioPacket>
|
||||
|
||||
private Map<String,RealBufferWaveRecorder> mRecorders = new HashMap<>();
|
||||
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
|
||||
private boolean mCanStartNewRecorders = true;
|
||||
|
||||
/**
|
||||
* Manages all audio recording for all processing channels. Reconstructs audio streams and distributes channel
|
||||
* audio to each of the recorders, starting and stopping the recorders as needed.
|
||||
*/
|
||||
public RecorderManager(ThreadPoolManager threadPoolManager)
|
||||
public RecorderManager()
|
||||
{
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mThreadPoolManager = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,7 +87,7 @@ public class RecorderManager implements Listener<AudioPacket>
|
||||
{
|
||||
recorder = new RealBufferWaveRecorder(AUDIO_SAMPLE_RATE, filePrefix);
|
||||
|
||||
recorder.start(mThreadPoolManager.getScheduledExecutorService());
|
||||
recorder.start(ThreadPool.SCHEDULED);
|
||||
|
||||
recorder.receive(audioPacket.getAudioBuffer());
|
||||
mRecorders.put(identifier, recorder);
|
||||
|
||||
@@ -19,37 +19,15 @@
|
||||
package record.mp3;
|
||||
|
||||
import audio.AudioPacket;
|
||||
import audio.IAudioPacketListener;
|
||||
import audio.broadcast.BroadcastModel;
|
||||
import audio.convert.MP3AudioConverter;
|
||||
import module.Module;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import properties.SystemProperties;
|
||||
import record.AudioRecorder;
|
||||
import record.wave.AudioPacketMonoWaveReader;
|
||||
import sample.Listener;
|
||||
import util.TimeStamp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* MP3 recorder for converting 8 kHz PCM audio packets to MP3 and writing to .mp3 file.
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
******************************************************************************/
|
||||
package settings;
|
||||
|
||||
import controller.ThreadPoolManager;
|
||||
import org.jdesktop.swingx.mapviewer.GeoPosition;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -27,6 +26,7 @@ import settings.ColorSetting.ColorSettingName;
|
||||
import source.recording.RecordingConfiguration;
|
||||
import source.tuner.configuration.TunerConfigurationEvent;
|
||||
import source.tuner.configuration.TunerConfigurationModel;
|
||||
import util.ThreadPool;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
@@ -54,18 +54,15 @@ public class SettingsManager implements Listener<TunerConfigurationEvent>
|
||||
private Settings mSettings = new Settings();
|
||||
private List<SettingChangeListener> mListeners = new ArrayList<>();
|
||||
private TunerConfigurationModel mTunerConfigurationModel;
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
private boolean mLoadingSettings = false;
|
||||
private AtomicBoolean mSettingsSavePending = new AtomicBoolean();
|
||||
|
||||
public SettingsManager(ThreadPoolManager threadPoolManager,
|
||||
TunerConfigurationModel tunerConfigurationModel)
|
||||
public SettingsManager(TunerConfigurationModel tunerConfigurationModel)
|
||||
{
|
||||
//TODO: move settings into a SettingsModel
|
||||
//and update this class to only provide loading, saving, and model
|
||||
//change detection producing a save.
|
||||
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
mTunerConfigurationModel = tunerConfigurationModel;
|
||||
|
||||
//Register for tuner config events so that we can save the settings
|
||||
@@ -481,8 +478,7 @@ public class SettingsManager implements Listener<TunerConfigurationEvent>
|
||||
{
|
||||
if(mSettingsSavePending.compareAndSet(false, true))
|
||||
{
|
||||
mThreadPoolManager.scheduleOnce(new SettingsSaveTask(),
|
||||
2, TimeUnit.SECONDS);
|
||||
ThreadPool.SCHEDULED.schedule(new SettingsSaveTask(), 2, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package source;
|
||||
|
||||
@@ -24,63 +25,60 @@ import source.mixer.MixerManager;
|
||||
import source.recording.RecordingSourceManager;
|
||||
import source.tuner.TunerManager;
|
||||
import source.tuner.TunerModel;
|
||||
import controller.ThreadPoolManager;
|
||||
|
||||
public class SourceManager
|
||||
public class SourceManager
|
||||
{
|
||||
private MixerManager mMixerManager;
|
||||
private RecordingSourceManager mRecordingSourceManager;
|
||||
private TunerManager mTunerManager;
|
||||
private TunerModel mTunerModel;
|
||||
private MixerManager mMixerManager;
|
||||
private RecordingSourceManager mRecordingSourceManager;
|
||||
private TunerManager mTunerManager;
|
||||
private TunerModel mTunerModel;
|
||||
|
||||
public SourceManager( TunerModel tunerModel,
|
||||
SettingsManager settingsManager,
|
||||
ThreadPoolManager threadPoolManager )
|
||||
{
|
||||
mTunerModel = tunerModel;
|
||||
mMixerManager = new MixerManager();
|
||||
mRecordingSourceManager = new RecordingSourceManager( settingsManager );
|
||||
mTunerManager = new TunerManager( mMixerManager, tunerModel, threadPoolManager );
|
||||
|
||||
//TODO: change mixer & recording managers to be models and hand them
|
||||
//in via the constructor. Perform loading outside of this class.
|
||||
}
|
||||
|
||||
public MixerManager getMixerManager()
|
||||
{
|
||||
return mMixerManager;
|
||||
}
|
||||
public SourceManager(TunerModel tunerModel, SettingsManager settingsManager)
|
||||
{
|
||||
mTunerModel = tunerModel;
|
||||
mMixerManager = new MixerManager();
|
||||
mRecordingSourceManager = new RecordingSourceManager(settingsManager);
|
||||
mTunerManager = new TunerManager(mMixerManager, tunerModel);
|
||||
|
||||
public RecordingSourceManager getRecordingSourceManager()
|
||||
{
|
||||
return mRecordingSourceManager;
|
||||
}
|
||||
|
||||
public TunerManager getTunerManager()
|
||||
{
|
||||
return mTunerManager;
|
||||
}
|
||||
|
||||
public Source getSource( SourceConfiguration config, int bandwidth )
|
||||
throws SourceException
|
||||
{
|
||||
Source retVal = null;
|
||||
//TODO: change mixer & recording managers to be models and hand them
|
||||
//in via the constructor. Perform loading outside of this class.
|
||||
}
|
||||
|
||||
switch( config.getSourceType() )
|
||||
{
|
||||
case MIXER:
|
||||
retVal = mMixerManager.getSource( config );
|
||||
break;
|
||||
case TUNER:
|
||||
retVal = mTunerModel.getSource( (SourceConfigTuner)config, bandwidth );
|
||||
break;
|
||||
case RECORDING:
|
||||
retVal = mRecordingSourceManager.getSource( config, bandwidth );
|
||||
case NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
public MixerManager getMixerManager()
|
||||
{
|
||||
return mMixerManager;
|
||||
}
|
||||
|
||||
public RecordingSourceManager getRecordingSourceManager()
|
||||
{
|
||||
return mRecordingSourceManager;
|
||||
}
|
||||
|
||||
public TunerManager getTunerManager()
|
||||
{
|
||||
return mTunerManager;
|
||||
}
|
||||
|
||||
public Source getSource(SourceConfiguration config, int bandwidth)
|
||||
throws SourceException
|
||||
{
|
||||
Source retVal = null;
|
||||
|
||||
switch(config.getSourceType())
|
||||
{
|
||||
case MIXER:
|
||||
retVal = mMixerManager.getSource(config);
|
||||
break;
|
||||
case TUNER:
|
||||
retVal = mTunerModel.getSource((SourceConfigTuner) config, bandwidth);
|
||||
break;
|
||||
case RECORDING:
|
||||
retVal = mRecordingSourceManager.getSource(config, bandwidth);
|
||||
case NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,38 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package source.tuner;
|
||||
|
||||
import dsp.filter.FilterFactory;
|
||||
import dsp.filter.Window.WindowType;
|
||||
import dsp.filter.cic.ComplexPrimeCICDecimate;
|
||||
import dsp.mixer.Oscillator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sample.Buffer;
|
||||
import sample.Listener;
|
||||
import sample.complex.Complex;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import source.ComplexSource;
|
||||
import source.SourceException;
|
||||
import source.tuner.frequency.FrequencyChangeEvent;
|
||||
import source.tuner.frequency.FrequencyChangeEvent.Event;
|
||||
import source.tuner.frequency.IFrequencyChangeProcessor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
@@ -26,371 +42,351 @@ import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Buffer;
|
||||
import sample.Listener;
|
||||
import sample.complex.Complex;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import source.ComplexSource;
|
||||
import source.SourceException;
|
||||
import source.tuner.frequency.FrequencyChangeEvent;
|
||||
import source.tuner.frequency.FrequencyChangeEvent.Event;
|
||||
import source.tuner.frequency.IFrequencyChangeProcessor;
|
||||
import dsp.filter.FilterFactory;
|
||||
import dsp.filter.Window.WindowType;
|
||||
import dsp.filter.cic.ComplexPrimeCICDecimate;
|
||||
import dsp.mixer.Oscillator;
|
||||
|
||||
public class TunerChannelSource extends ComplexSource
|
||||
implements IFrequencyChangeProcessor, Listener<ComplexBuffer>
|
||||
implements IFrequencyChangeProcessor, Listener<ComplexBuffer>
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( TunerChannelSource.class );
|
||||
|
||||
private static int CHANNEL_RATE = 48000;
|
||||
private static int CHANNEL_PASS_FREQUENCY = 12000;
|
||||
|
||||
private LinkedTransferQueue<ComplexBuffer> mBuffer =
|
||||
new LinkedTransferQueue<ComplexBuffer>();
|
||||
private Tuner mTuner;
|
||||
private TunerChannel mTunerChannel;
|
||||
private Oscillator mMixer;
|
||||
private ComplexPrimeCICDecimate mDecimationFilter;
|
||||
private Listener<ComplexBuffer> mListener;
|
||||
private IFrequencyChangeProcessor mFrequencyChangeProcessor;
|
||||
private ScheduledFuture<?> mTaskHandle;
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger(TunerChannelSource.class);
|
||||
|
||||
private long mTunerFrequency = 0;
|
||||
private int mTunerSampleRate;
|
||||
private int mChannelFrequencyCorrection = 0;
|
||||
|
||||
private DecimationProcessor mDecimationProcessor = new DecimationProcessor();
|
||||
|
||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||
private boolean mExpended = false;
|
||||
|
||||
/**
|
||||
* Provides a Digital Drop Channel (DDC) to decimate the IQ output from a
|
||||
* tuner down to a 48 kHz IQ channel rate.
|
||||
*
|
||||
* Note: this class can only be used once (started and stopped) and a new
|
||||
* tuner channel source must be requested from the tuner once this object
|
||||
* has been stopped. This is because channels are managed dynamically and
|
||||
* center tuned frequency may have changed since this source was obtained
|
||||
* and thus the tuner might no longer be able to source this channel once it
|
||||
* has been stopped.
|
||||
*
|
||||
* @param tuner to obtain wideband IQ samples from
|
||||
* @param tunerChannel specifying the center frequency for the DDC
|
||||
* @throws RejectedExecutionException if the thread pool manager cannot
|
||||
* accept the decimation processing task
|
||||
* @throws SourceException if the tuner has an issue providing IQ samples
|
||||
*/
|
||||
public TunerChannelSource( Tuner tuner, TunerChannel tunerChannel )
|
||||
throws RejectedExecutionException, SourceException
|
||||
private static int CHANNEL_RATE = 48000;
|
||||
private static int CHANNEL_PASS_FREQUENCY = 12000;
|
||||
|
||||
private LinkedTransferQueue<ComplexBuffer> mBuffer =
|
||||
new LinkedTransferQueue<ComplexBuffer>();
|
||||
private Tuner mTuner;
|
||||
private TunerChannel mTunerChannel;
|
||||
private Oscillator mMixer;
|
||||
private ComplexPrimeCICDecimate mDecimationFilter;
|
||||
private Listener<ComplexBuffer> mListener;
|
||||
private IFrequencyChangeProcessor mFrequencyChangeProcessor;
|
||||
private ScheduledFuture<?> mTaskHandle;
|
||||
|
||||
private long mTunerFrequency = 0;
|
||||
private int mTunerSampleRate;
|
||||
private int mChannelFrequencyCorrection = 0;
|
||||
|
||||
private DecimationProcessor mDecimationProcessor = new DecimationProcessor();
|
||||
|
||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||
private boolean mExpended = false;
|
||||
|
||||
/**
|
||||
* Provides a Digital Drop Channel (DDC) to decimate the IQ output from a
|
||||
* tuner down to a 48 kHz IQ channel rate.
|
||||
*
|
||||
* Note: this class can only be used once (started and stopped) and a new
|
||||
* tuner channel source must be requested from the tuner once this object
|
||||
* has been stopped. This is because channels are managed dynamically and
|
||||
* center tuned frequency may have changed since this source was obtained
|
||||
* and thus the tuner might no longer be able to source this channel once it
|
||||
* has been stopped.
|
||||
*
|
||||
* @param tuner to obtain wideband IQ samples from
|
||||
* @param tunerChannel specifying the center frequency for the DDC
|
||||
* @throws RejectedExecutionException if the thread pool manager cannot
|
||||
* accept the decimation processing task
|
||||
* @throws SourceException if the tuner has an issue providing IQ samples
|
||||
*/
|
||||
public TunerChannelSource(Tuner tuner, TunerChannel tunerChannel)
|
||||
throws RejectedExecutionException, SourceException
|
||||
{
|
||||
mTuner = tuner;
|
||||
mTuner.getTunerController().addListener( (IFrequencyChangeProcessor)this );
|
||||
|
||||
mTunerChannel = tunerChannel;
|
||||
mTuner = tuner;
|
||||
mTuner.getTunerController().addListener((IFrequencyChangeProcessor) this);
|
||||
|
||||
mTunerChannel = tunerChannel;
|
||||
|
||||
mTunerFrequency = mTuner.getTunerController().getFrequency();
|
||||
|
||||
mTunerFrequency = mTuner.getTunerController().getFrequency();
|
||||
|
||||
/* Setup the frequency translator to the current source frequency */
|
||||
long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency();
|
||||
|
||||
mMixer = new Oscillator( frequencyOffset,
|
||||
mTuner.getTunerController().getSampleRate() );
|
||||
long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency();
|
||||
|
||||
mMixer = new Oscillator(frequencyOffset,
|
||||
mTuner.getTunerController().getSampleRate());
|
||||
|
||||
/* Fire a sample rate change event to setup the decimation chain */
|
||||
frequencyChanged( new FrequencyChangeEvent(
|
||||
Event.NOTIFICATION_SAMPLE_RATE_CHANGE,
|
||||
mTuner.getTunerController().getSampleRate() ) );
|
||||
frequencyChanged(new FrequencyChangeEvent(
|
||||
Event.NOTIFICATION_SAMPLE_RATE_CHANGE,
|
||||
mTuner.getTunerController().getSampleRate()));
|
||||
}
|
||||
|
||||
|
||||
public void start( ScheduledExecutorService executor )
|
||||
{
|
||||
if( mExpended )
|
||||
{
|
||||
throw new IllegalStateException( "Attempt to re-start an expended "
|
||||
+ "tuner channel source. TunerChannelSource objects can only "
|
||||
+ "be used once. " );
|
||||
}
|
||||
|
||||
if( mRunning.compareAndSet( false, true ))
|
||||
{
|
||||
|
||||
|
||||
public void start(ScheduledExecutorService executor)
|
||||
{
|
||||
if(mExpended)
|
||||
{
|
||||
throw new IllegalStateException("Attempt to re-start an expended "
|
||||
+ "tuner channel source. TunerChannelSource objects can only "
|
||||
+ "be used once. ");
|
||||
}
|
||||
|
||||
if(mRunning.compareAndSet(false, true))
|
||||
{
|
||||
/* Schedule the decimation task to run every 10 ms (100 iterations/second) */
|
||||
mTaskHandle = executor.scheduleAtFixedRate( mDecimationProcessor,
|
||||
0, 10, TimeUnit.MILLISECONDS );
|
||||
mTaskHandle = executor.scheduleAtFixedRate(mDecimationProcessor,
|
||||
0, 10, TimeUnit.MILLISECONDS);
|
||||
|
||||
/* Finally, register to receive samples from the tuner */
|
||||
mTuner.addListener( (Listener<ComplexBuffer>)this );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.warn( "Attempt to start() and already running tuner channel source was ignored" );
|
||||
}
|
||||
}
|
||||
mTuner.addListener((Listener<ComplexBuffer>) this);
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.warn("Attempt to start() and already running tuner channel source was ignored");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
}
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
if( mRunning.compareAndSet( true, false ))
|
||||
{
|
||||
mTuner.releaseChannel( this );
|
||||
mDecimationProcessor.shutdown();
|
||||
|
||||
if( mTaskHandle != null )
|
||||
{
|
||||
mTaskHandle.cancel( true );
|
||||
mTaskHandle = null;
|
||||
}
|
||||
|
||||
mBuffer.clear();
|
||||
|
||||
mExpended = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.warn( "Attempt to stop() and already stopped tuner channel source was ignored" );
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
if(mRunning.compareAndSet(true, false))
|
||||
{
|
||||
mTuner.releaseChannel(this);
|
||||
mDecimationProcessor.shutdown();
|
||||
|
||||
if(mTaskHandle != null)
|
||||
{
|
||||
mTaskHandle.cancel(true);
|
||||
mTaskHandle = null;
|
||||
}
|
||||
|
||||
mBuffer.clear();
|
||||
|
||||
mExpended = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.warn("Attempt to stop() and already stopped tuner channel source was ignored");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
if( !mRunning.get() )
|
||||
{
|
||||
if(!mRunning.get())
|
||||
{
|
||||
/* Tell the tuner to release/unregister our resources */
|
||||
mTuner.getTunerController().removeListener( this );
|
||||
mTuner = null;
|
||||
mTunerChannel = null;
|
||||
mBuffer = null;
|
||||
mFrequencyChangeProcessor = null;
|
||||
mListener = null;
|
||||
mMixer = null;
|
||||
mDecimationFilter.dispose();
|
||||
mDecimationFilter = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Tuner getTuner()
|
||||
{
|
||||
return mTuner;
|
||||
}
|
||||
|
||||
public TunerChannel getTunerChannel()
|
||||
{
|
||||
return mTunerChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( ComplexBuffer buffer )
|
||||
{
|
||||
if( mRunning.get() )
|
||||
{
|
||||
mBuffer.add( buffer );
|
||||
}
|
||||
mTuner.getTunerController().removeListener(this);
|
||||
// mTuner = null;
|
||||
// mTunerChannel = null;
|
||||
// mBuffer = null;
|
||||
// mFrequencyChangeProcessor = null;
|
||||
// mListener = null;
|
||||
// mMixer = null;
|
||||
// mDecimationFilter.dispose();
|
||||
// mDecimationFilter = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setFrequencyChangeListener( IFrequencyChangeProcessor processor )
|
||||
public Tuner getTuner()
|
||||
{
|
||||
mFrequencyChangeProcessor = processor;
|
||||
return mTuner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
public TunerChannel getTunerChannel()
|
||||
{
|
||||
return mTunerChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ComplexBuffer buffer)
|
||||
{
|
||||
if(mRunning.get())
|
||||
{
|
||||
mBuffer.add(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFrequencyChangeListener(IFrequencyChangeProcessor processor)
|
||||
{
|
||||
mFrequencyChangeProcessor = processor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListener(Listener<ComplexBuffer> listener)
|
||||
{
|
||||
/* Save a pointer to the listener so that if we have to change the
|
||||
* decimation filter, we can re-add the listener */
|
||||
mListener = listener;
|
||||
|
||||
mDecimationFilter.setListener( listener );
|
||||
}
|
||||
mListener = listener;
|
||||
|
||||
@Override
|
||||
public void removeListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mDecimationFilter.removeListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for frequency change events received from the tuner and channel
|
||||
* frequency correction events received from the channel consumer/listener
|
||||
*/
|
||||
@Override
|
||||
public void frequencyChanged( FrequencyChangeEvent event ) throws SourceException
|
||||
{
|
||||
// Echo the event to the registered event listener
|
||||
if( mFrequencyChangeProcessor != null )
|
||||
{
|
||||
mFrequencyChangeProcessor.frequencyChanged( event );
|
||||
}
|
||||
|
||||
switch( event.getEvent() )
|
||||
{
|
||||
case NOTIFICATION_FREQUENCY_CHANGE:
|
||||
mTunerFrequency = event.getValue().longValue();
|
||||
updateMixerFrequencyOffset();
|
||||
break;
|
||||
case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
|
||||
mChannelFrequencyCorrection = event.getValue().intValue();
|
||||
|
||||
updateMixerFrequencyOffset();
|
||||
|
||||
if( mFrequencyChangeProcessor != null )
|
||||
{
|
||||
mFrequencyChangeProcessor.frequencyChanged(
|
||||
new FrequencyChangeEvent(
|
||||
Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE,
|
||||
mChannelFrequencyCorrection ) );
|
||||
}
|
||||
break;
|
||||
case NOTIFICATION_SAMPLE_RATE_CHANGE:
|
||||
int sampleRate = event.getValue().intValue();
|
||||
|
||||
if( mTunerSampleRate != sampleRate )
|
||||
{
|
||||
mMixer.setSampleRate( sampleRate );
|
||||
|
||||
/* Get new decimation filter */
|
||||
mDecimationFilter = FilterFactory
|
||||
.getDecimationFilter( sampleRate,
|
||||
CHANNEL_RATE,
|
||||
1, //Order
|
||||
CHANNEL_PASS_FREQUENCY,
|
||||
60, //dB attenuation
|
||||
WindowType.HAMMING );
|
||||
|
||||
/* re-add the original output listener */
|
||||
mDecimationFilter.setListener( mListener );
|
||||
|
||||
mTunerSampleRate = sampleRate;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mDecimationFilter.setListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the local mixer frequency offset from the tuned frequency,
|
||||
* channel's requested frequency, and channel frequency correction.
|
||||
*/
|
||||
private void updateMixerFrequencyOffset()
|
||||
{
|
||||
long offset = mTunerFrequency -
|
||||
mTunerChannel.getFrequency() -
|
||||
mChannelFrequencyCorrection;
|
||||
@Override
|
||||
public void removeListener(Listener<ComplexBuffer> listener)
|
||||
{
|
||||
mDecimationFilter.removeListener();
|
||||
}
|
||||
|
||||
mMixer.setFrequency( offset );
|
||||
}
|
||||
/**
|
||||
* Handler for frequency change events received from the tuner and channel
|
||||
* frequency correction events received from the channel consumer/listener
|
||||
*/
|
||||
@Override
|
||||
public void frequencyChanged(FrequencyChangeEvent event) throws SourceException
|
||||
{
|
||||
// Echo the event to the registered event listener
|
||||
if(mFrequencyChangeProcessor != null)
|
||||
{
|
||||
mFrequencyChangeProcessor.frequencyChanged(event);
|
||||
}
|
||||
|
||||
switch(event.getEvent())
|
||||
{
|
||||
case NOTIFICATION_FREQUENCY_CHANGE:
|
||||
mTunerFrequency = event.getValue().longValue();
|
||||
updateMixerFrequencyOffset();
|
||||
break;
|
||||
case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
|
||||
mChannelFrequencyCorrection = event.getValue().intValue();
|
||||
|
||||
updateMixerFrequencyOffset();
|
||||
|
||||
if(mFrequencyChangeProcessor != null)
|
||||
{
|
||||
mFrequencyChangeProcessor.frequencyChanged(
|
||||
new FrequencyChangeEvent(
|
||||
Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE,
|
||||
mChannelFrequencyCorrection));
|
||||
}
|
||||
break;
|
||||
case NOTIFICATION_SAMPLE_RATE_CHANGE:
|
||||
int sampleRate = event.getValue().intValue();
|
||||
|
||||
if(mTunerSampleRate != sampleRate)
|
||||
{
|
||||
mMixer.setSampleRate(sampleRate);
|
||||
|
||||
/* Get new decimation filter */
|
||||
mDecimationFilter = FilterFactory
|
||||
.getDecimationFilter(sampleRate,
|
||||
CHANNEL_RATE,
|
||||
1, //Order
|
||||
CHANNEL_PASS_FREQUENCY,
|
||||
60, //dB attenuation
|
||||
WindowType.HAMMING);
|
||||
|
||||
/* re-add the original output listener */
|
||||
mDecimationFilter.setListener(mListener);
|
||||
|
||||
mTunerSampleRate = sampleRate;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the local mixer frequency offset from the tuned frequency,
|
||||
* channel's requested frequency, and channel frequency correction.
|
||||
*/
|
||||
private void updateMixerFrequencyOffset()
|
||||
{
|
||||
long offset = mTunerFrequency -
|
||||
mTunerChannel.getFrequency() -
|
||||
mChannelFrequencyCorrection;
|
||||
|
||||
mMixer.setFrequency(offset);
|
||||
}
|
||||
|
||||
public int getSampleRate() throws SourceException
|
||||
{
|
||||
return CHANNEL_RATE;
|
||||
return CHANNEL_RATE;
|
||||
}
|
||||
|
||||
public long getFrequency() throws SourceException
|
||||
{
|
||||
return mTunerChannel.getFrequency();
|
||||
return mTunerChannel.getFrequency();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decimates an inbound buffer of I/Q samples from the source down to the
|
||||
* standard 48000 channel sample rate
|
||||
*/
|
||||
public class DecimationProcessor implements Runnable
|
||||
{
|
||||
private boolean mProcessing = true;
|
||||
private List<ComplexBuffer> mSampleBuffers = new ArrayList<ComplexBuffer>();
|
||||
public class DecimationProcessor implements Runnable
|
||||
{
|
||||
private boolean mProcessing = true;
|
||||
private List<ComplexBuffer> mSampleBuffers = new ArrayList<ComplexBuffer>();
|
||||
|
||||
public void shutdown()
|
||||
{
|
||||
mProcessing = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown()
|
||||
{
|
||||
mProcessing = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
/* General exception handler so that any errors won't kill the
|
||||
* decimation thread and cause the input buffers to fill up and
|
||||
* run the program out of memory */
|
||||
try
|
||||
{
|
||||
if( mProcessing )
|
||||
{
|
||||
{
|
||||
// long start = System.currentTimeMillis();
|
||||
|
||||
mBuffer.drainTo( mSampleBuffers, 8 );
|
||||
|
||||
for( Buffer buffer: mSampleBuffers )
|
||||
{
|
||||
try
|
||||
{
|
||||
if(mProcessing)
|
||||
{
|
||||
{
|
||||
mBuffer.drainTo(mSampleBuffers, 8);
|
||||
|
||||
for(Buffer buffer : mSampleBuffers)
|
||||
{
|
||||
/* Check to see if we've been shutdown */
|
||||
if( !mProcessing )
|
||||
{
|
||||
mBuffer.clear();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
float[] samples = buffer.getSamples();
|
||||
if(!mProcessing)
|
||||
{
|
||||
mBuffer.clear();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
float[] samples = buffer.getSamples();
|
||||
|
||||
/* We make a copy of the buffer so that we don't affect
|
||||
* anyone else that is using the same buffer, like other
|
||||
* channels or the spectral display */
|
||||
float[] translated = new float[ samples.length ];
|
||||
float[] translated = new float[samples.length];
|
||||
|
||||
/* Perform frequency translation */
|
||||
for( int x = 0; x < samples.length; x += 2 )
|
||||
{
|
||||
mMixer.rotate();
|
||||
|
||||
translated[ x ] = Complex.multiplyInphase(
|
||||
samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() );
|
||||
for(int x = 0; x < samples.length; x += 2)
|
||||
{
|
||||
mMixer.rotate();
|
||||
|
||||
translated[ x + 1 ] = Complex.multiplyQuadrature(
|
||||
samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() );
|
||||
}
|
||||
|
||||
if( mProcessing && mDecimationFilter != null )
|
||||
{
|
||||
mDecimationFilter.receive( new ComplexBuffer( translated ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mLog.debug( "Processed [" + mSampleBuffers.size() + "] in " + ( System.currentTimeMillis() - start ) );
|
||||
|
||||
mSampleBuffers.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
translated[x] = Complex.multiplyInphase(
|
||||
samples[x], samples[x + 1], mMixer.inphase(), mMixer.quadrature());
|
||||
|
||||
translated[x + 1] = Complex.multiplyQuadrature(
|
||||
samples[x], samples[x + 1], mMixer.inphase(), mMixer.quadrature());
|
||||
}
|
||||
|
||||
if(mProcessing)
|
||||
{
|
||||
final ComplexPrimeCICDecimate filter = mDecimationFilter;
|
||||
filter.receive(new ComplexBuffer(translated));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mSampleBuffers.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
/* Only log the stack trace if we're still processing */
|
||||
if( mProcessing )
|
||||
{
|
||||
mLog.error( "Error encountered during decimation process", e );
|
||||
}
|
||||
}
|
||||
if(mProcessing)
|
||||
{
|
||||
mLog.error("Error encountered during decimation process", e);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check to see if we've been shutdown */
|
||||
if( !mProcessing )
|
||||
{
|
||||
mBuffer.clear();
|
||||
mSampleBuffers.clear();
|
||||
}
|
||||
if(!mProcessing)
|
||||
{
|
||||
mBuffer.clear();
|
||||
mSampleBuffers.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package source.tuner;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.usb4java.Device;
|
||||
import org.usb4java.DeviceDescriptor;
|
||||
import org.usb4java.DeviceList;
|
||||
import org.usb4java.LibUsb;
|
||||
|
||||
import source.SourceException;
|
||||
import source.mixer.MixerManager;
|
||||
import source.tuner.airspy.AirspyTuner;
|
||||
@@ -39,437 +37,424 @@ import source.tuner.rtl.RTL2832Tuner;
|
||||
import source.tuner.rtl.RTL2832TunerController;
|
||||
import source.tuner.rtl.e4k.E4KTunerController;
|
||||
import source.tuner.rtl.r820t.R820TTunerController;
|
||||
import controller.ThreadPoolManager;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class TunerManager
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( TunerManager.class );
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger(TunerManager.class);
|
||||
|
||||
private MixerManager mMixerManager;
|
||||
private TunerModel mTunerModel;
|
||||
|
||||
public TunerManager(MixerManager mixerManager, TunerModel tunerModel)
|
||||
{
|
||||
mMixerManager = mixerManager;
|
||||
mTunerModel = tunerModel;
|
||||
|
||||
initTuners();
|
||||
}
|
||||
|
||||
private MixerManager mMixerManager;
|
||||
private TunerModel mTunerModel;
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
|
||||
public TunerManager( MixerManager mixerManager,
|
||||
TunerModel tunerModel,
|
||||
ThreadPoolManager threadPoolManager )
|
||||
{
|
||||
mMixerManager = mixerManager;
|
||||
mTunerModel = tunerModel;
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
|
||||
initTuners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs cleanup of USB related issues
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
LibUsb.exit( null );
|
||||
LibUsb.exit(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all USB tuners and USB/Mixer tuner devices
|
||||
*/
|
||||
private void initTuners()
|
||||
{
|
||||
DeviceList deviceList = new DeviceList();
|
||||
private void initTuners()
|
||||
{
|
||||
DeviceList deviceList = new DeviceList();
|
||||
|
||||
int result = LibUsb.init( null );
|
||||
int result = LibUsb.init(null);
|
||||
|
||||
if( result != LibUsb.SUCCESS )
|
||||
{
|
||||
mLog.error( "unable to initialize libusb [" +
|
||||
LibUsb.errorName( result ) + "]" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.info( "LibUSB API Version: " + LibUsb.getApiVersion() );
|
||||
mLog.info( "LibUSB Version: " + LibUsb.getVersion() );
|
||||
|
||||
result = LibUsb.getDeviceList( null, deviceList );
|
||||
|
||||
if( result < 0 )
|
||||
{
|
||||
mLog.error( "unable to get device list from libusb [" + result + " / " +
|
||||
LibUsb.errorName( result ) + "]" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.info( "discovered [" + result + "] attached USB devices" );
|
||||
}
|
||||
}
|
||||
if(result != LibUsb.SUCCESS)
|
||||
{
|
||||
mLog.error("unable to initialize libusb [" +
|
||||
LibUsb.errorName(result) + "]");
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.info("LibUSB API Version: " + LibUsb.getApiVersion());
|
||||
mLog.info("LibUSB Version: " + LibUsb.getVersion());
|
||||
|
||||
for( Device device: deviceList )
|
||||
{
|
||||
DeviceDescriptor descriptor = new DeviceDescriptor();
|
||||
|
||||
result = LibUsb.getDeviceDescriptor( device, descriptor );
|
||||
|
||||
if( result != LibUsb.SUCCESS )
|
||||
{
|
||||
mLog.error( "unable to read device descriptor [" +
|
||||
LibUsb.errorName( result ) + "]" );
|
||||
}
|
||||
else
|
||||
{
|
||||
TunerInitStatus status = initTuner( device, descriptor );
|
||||
result = LibUsb.getDeviceList(null, deviceList);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "usb device [" );
|
||||
sb.append( String.format( "%04X", descriptor.idVendor() ) );
|
||||
sb.append( ":" );
|
||||
sb.append( String.format( "%04X", descriptor.idProduct() ) );
|
||||
|
||||
if( status.isLoaded() )
|
||||
{
|
||||
Tuner tuner = status.getTuner();
|
||||
|
||||
try
|
||||
{
|
||||
mTunerModel.addTuner( tuner );
|
||||
sb.append( "] LOADED: " );
|
||||
sb.append( tuner.toString() );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
sb.append( "] NOT LOADED: " );
|
||||
sb.append( status.getInfo() );
|
||||
sb.append( " Error:" + e.getMessage() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( "] NOT LOADED: " );
|
||||
sb.append( status.getInfo() );
|
||||
}
|
||||
|
||||
mLog.info( sb.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
LibUsb.freeDeviceList( deviceList, true );
|
||||
}
|
||||
|
||||
private TunerInitStatus initTuner( Device device,
|
||||
DeviceDescriptor descriptor )
|
||||
{
|
||||
if( device != null && descriptor != null )
|
||||
{
|
||||
TunerClass tunerClass = TunerClass.valueOf( descriptor.idVendor(),
|
||||
descriptor.idProduct() );
|
||||
|
||||
switch( tunerClass )
|
||||
{
|
||||
case AIRSPY:
|
||||
return initAirspyTuner( device, descriptor );
|
||||
case ETTUS_USRP_B100:
|
||||
return initEttusB100Tuner( device, descriptor );
|
||||
case FUNCUBE_DONGLE_PRO:
|
||||
return initFuncubeProTuner( device, descriptor );
|
||||
case FUNCUBE_DONGLE_PRO_PLUS:
|
||||
return initFuncubeProPlusTuner( device, descriptor );
|
||||
case HACKRF_ONE:
|
||||
case RAD1O:
|
||||
return initHackRFTuner( device, descriptor );
|
||||
case COMPRO_VIDEOMATE_U620F:
|
||||
case COMPRO_VIDEOMATE_U650F:
|
||||
case COMPRO_VIDEOMATE_U680F:
|
||||
case GENERIC_2832:
|
||||
case GENERIC_2838:
|
||||
case DEXATEK_5217_DVBT:
|
||||
case DEXATEK_DIGIVOX_MINI_II_REV3:
|
||||
case DEXATEK_LOGILINK_VG002A:
|
||||
case GIGABYTE_GTU7300:
|
||||
case GTEK_T803:
|
||||
case LIFEVIEW_LV5T_DELUXE:
|
||||
case MYGICA_TD312:
|
||||
case PEAK_102569AGPK:
|
||||
case PROLECTRIX_DV107669:
|
||||
case SVEON_STV20:
|
||||
case TERRATEC_CINERGY_T_REV1:
|
||||
case TERRATEC_CINERGY_T_REV3:
|
||||
case TERRATEC_NOXON_REV1_B3:
|
||||
case TERRATEC_NOXON_REV1_B4:
|
||||
case TERRATEC_NOXON_REV1_B7:
|
||||
case TERRATEC_NOXON_REV1_C6:
|
||||
case TERRATEC_NOXON_REV2:
|
||||
case TERRATEC_T_STICK_PLUS:
|
||||
case TWINTECH_UT40:
|
||||
case ZAAPA_ZTMINDVBZP:
|
||||
return initRTL2832Tuner( tunerClass, device, descriptor );
|
||||
case UNKNOWN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new TunerInitStatus( null, "Unknown Device" );
|
||||
}
|
||||
|
||||
private TunerInitStatus initAirspyTuner( Device device,
|
||||
DeviceDescriptor descriptor )
|
||||
{
|
||||
try
|
||||
{
|
||||
AirspyTunerController airspyController =
|
||||
new AirspyTunerController( device, mThreadPoolManager );
|
||||
|
||||
airspyController.init();
|
||||
|
||||
AirspyTuner tuner = new AirspyTuner( airspyController );
|
||||
|
||||
return new TunerInitStatus( tuner, "LOADED" );
|
||||
}
|
||||
catch( SourceException se )
|
||||
{
|
||||
mLog.error( "couldn't construct Airspy controller/tuner", se );
|
||||
|
||||
return new TunerInitStatus( null,
|
||||
"error constructing Airspy tuner controller" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private TunerInitStatus initEttusB100Tuner( Device device,
|
||||
DeviceDescriptor descriptor )
|
||||
{
|
||||
return new TunerInitStatus( null, "Ettus B100 tuner not currently "
|
||||
+ "supported" );
|
||||
}
|
||||
|
||||
private TunerInitStatus initFuncubeProTuner( Device device,
|
||||
DeviceDescriptor descriptor )
|
||||
{
|
||||
String reason = "NOT LOADED";
|
||||
|
||||
MixerTunerDataLine dataline = getMixerTunerDataLine(
|
||||
TunerClass.FUNCUBE_DONGLE_PRO.getTunerType() );
|
||||
|
||||
if( dataline != null )
|
||||
{
|
||||
FCD1TunerController controller =
|
||||
new FCD1TunerController( device, descriptor );
|
||||
|
||||
try
|
||||
if(result < 0)
|
||||
{
|
||||
controller.init();
|
||||
|
||||
FCDTuner tuner =
|
||||
new FCDTuner( dataline, controller );
|
||||
|
||||
return new TunerInitStatus( tuner, "LOADED" );
|
||||
mLog.error("unable to get device list from libusb [" + result + " / " +
|
||||
LibUsb.errorName(result) + "]");
|
||||
}
|
||||
catch ( SourceException e )
|
||||
else
|
||||
{
|
||||
mLog.error( "couldn't load funcube dongle pro tuner", e );
|
||||
|
||||
reason = "error during initialization - " + e.getLocalizedMessage();
|
||||
mLog.info("discovered [" + result + "] attached USB devices");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reason = "couldn't find matching mixer dataline";
|
||||
}
|
||||
}
|
||||
|
||||
return new TunerInitStatus( null, "Funcube Dongle Pro tuner not "
|
||||
+ "loaded - " + reason );
|
||||
}
|
||||
for(Device device : deviceList)
|
||||
{
|
||||
DeviceDescriptor descriptor = new DeviceDescriptor();
|
||||
|
||||
private TunerInitStatus initFuncubeProPlusTuner( Device device,
|
||||
DeviceDescriptor descriptor )
|
||||
{
|
||||
String reason = "NOT LOADED";
|
||||
|
||||
MixerTunerDataLine dataline = getMixerTunerDataLine(
|
||||
TunerClass.FUNCUBE_DONGLE_PRO_PLUS.getTunerType() );
|
||||
|
||||
if( dataline != null )
|
||||
{
|
||||
FCD2TunerController controller =
|
||||
new FCD2TunerController( device, descriptor );
|
||||
result = LibUsb.getDeviceDescriptor(device, descriptor);
|
||||
|
||||
try
|
||||
if(result != LibUsb.SUCCESS)
|
||||
{
|
||||
controller.init();
|
||||
|
||||
FCDTuner tuner =
|
||||
new FCDTuner( dataline, controller );
|
||||
|
||||
return new TunerInitStatus( tuner, "LOADED" );
|
||||
mLog.error("unable to read device descriptor [" +
|
||||
LibUsb.errorName(result) + "]");
|
||||
}
|
||||
catch ( SourceException e )
|
||||
else
|
||||
{
|
||||
mLog.error( "couldn't load funcube dongle pro plus tuner", e );
|
||||
|
||||
reason = "error during initialization - " +
|
||||
e.getLocalizedMessage();
|
||||
TunerInitStatus status = initTuner(device, descriptor);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("usb device [");
|
||||
sb.append(String.format("%04X", descriptor.idVendor()));
|
||||
sb.append(":");
|
||||
sb.append(String.format("%04X", descriptor.idProduct()));
|
||||
|
||||
if(status.isLoaded())
|
||||
{
|
||||
Tuner tuner = status.getTuner();
|
||||
|
||||
try
|
||||
{
|
||||
mTunerModel.addTuner(tuner);
|
||||
sb.append("] LOADED: ");
|
||||
sb.append(tuner.toString());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
sb.append("] NOT LOADED: ");
|
||||
sb.append(status.getInfo());
|
||||
sb.append(" Error:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("] NOT LOADED: ");
|
||||
sb.append(status.getInfo());
|
||||
}
|
||||
|
||||
mLog.info(sb.toString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reason = "couldn't find matching mixer dataline";
|
||||
}
|
||||
}
|
||||
|
||||
return new TunerInitStatus( null, "Funcube Dongle Pro tuner not "
|
||||
+ "loaded - " + reason );
|
||||
}
|
||||
LibUsb.freeDeviceList(deviceList, true);
|
||||
}
|
||||
|
||||
private TunerInitStatus initHackRFTuner( Device device,
|
||||
DeviceDescriptor descriptor )
|
||||
{
|
||||
try
|
||||
{
|
||||
HackRFTunerController hackRFController =
|
||||
new HackRFTunerController( device, descriptor,
|
||||
mThreadPoolManager );
|
||||
|
||||
hackRFController.init();
|
||||
|
||||
HackRFTuner tuner = new HackRFTuner( hackRFController );
|
||||
|
||||
return new TunerInitStatus( tuner, "LOADED" );
|
||||
}
|
||||
catch( SourceException se )
|
||||
{
|
||||
mLog.error( "couldn't construct HackRF controller/tuner", se );
|
||||
|
||||
return new TunerInitStatus( null,
|
||||
"error constructing HackRF tuner controller" );
|
||||
}
|
||||
}
|
||||
private TunerInitStatus initTuner(Device device,
|
||||
DeviceDescriptor descriptor)
|
||||
{
|
||||
if(device != null && descriptor != null)
|
||||
{
|
||||
TunerClass tunerClass = TunerClass.valueOf(descriptor.idVendor(),
|
||||
descriptor.idProduct());
|
||||
|
||||
private TunerInitStatus initRTL2832Tuner( TunerClass tunerClass,
|
||||
Device device,
|
||||
DeviceDescriptor deviceDescriptor )
|
||||
{
|
||||
String reason = "NOT LOADED";
|
||||
switch(tunerClass)
|
||||
{
|
||||
case AIRSPY:
|
||||
return initAirspyTuner(device, descriptor);
|
||||
case ETTUS_USRP_B100:
|
||||
return initEttusB100Tuner(device, descriptor);
|
||||
case FUNCUBE_DONGLE_PRO:
|
||||
return initFuncubeProTuner(device, descriptor);
|
||||
case FUNCUBE_DONGLE_PRO_PLUS:
|
||||
return initFuncubeProPlusTuner(device, descriptor);
|
||||
case HACKRF_ONE:
|
||||
case RAD1O:
|
||||
return initHackRFTuner(device, descriptor);
|
||||
case COMPRO_VIDEOMATE_U620F:
|
||||
case COMPRO_VIDEOMATE_U650F:
|
||||
case COMPRO_VIDEOMATE_U680F:
|
||||
case GENERIC_2832:
|
||||
case GENERIC_2838:
|
||||
case DEXATEK_5217_DVBT:
|
||||
case DEXATEK_DIGIVOX_MINI_II_REV3:
|
||||
case DEXATEK_LOGILINK_VG002A:
|
||||
case GIGABYTE_GTU7300:
|
||||
case GTEK_T803:
|
||||
case LIFEVIEW_LV5T_DELUXE:
|
||||
case MYGICA_TD312:
|
||||
case PEAK_102569AGPK:
|
||||
case PROLECTRIX_DV107669:
|
||||
case SVEON_STV20:
|
||||
case TERRATEC_CINERGY_T_REV1:
|
||||
case TERRATEC_CINERGY_T_REV3:
|
||||
case TERRATEC_NOXON_REV1_B3:
|
||||
case TERRATEC_NOXON_REV1_B4:
|
||||
case TERRATEC_NOXON_REV1_B7:
|
||||
case TERRATEC_NOXON_REV1_C6:
|
||||
case TERRATEC_NOXON_REV2:
|
||||
case TERRATEC_T_STICK_PLUS:
|
||||
case TWINTECH_UT40:
|
||||
case ZAAPA_ZTMINDVBZP:
|
||||
return initRTL2832Tuner(tunerClass, device, descriptor);
|
||||
case UNKNOWN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TunerType tunerType = tunerClass.getTunerType();
|
||||
|
||||
if( tunerType == TunerType.RTL2832_VARIOUS )
|
||||
{
|
||||
try
|
||||
{
|
||||
tunerType = RTL2832TunerController.identifyTunerType( device );
|
||||
}
|
||||
catch( SourceException e )
|
||||
{
|
||||
mLog.error( "couldn't determine RTL2832 tuner type", e );
|
||||
tunerType = TunerType.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
switch( tunerType )
|
||||
{
|
||||
case ELONICS_E4000:
|
||||
try
|
||||
{
|
||||
E4KTunerController controller =
|
||||
new E4KTunerController( device, deviceDescriptor,
|
||||
mThreadPoolManager );
|
||||
|
||||
controller.init();
|
||||
|
||||
RTL2832Tuner rtlTuner =
|
||||
new RTL2832Tuner( tunerClass, controller );
|
||||
|
||||
return new TunerInitStatus( rtlTuner, "LOADED" );
|
||||
}
|
||||
catch( SourceException se )
|
||||
{
|
||||
return new TunerInitStatus( null, "Error constructing E4K tuner "
|
||||
+ "controller - " + se.getLocalizedMessage() );
|
||||
}
|
||||
case RAFAELMICRO_R820T:
|
||||
try
|
||||
{
|
||||
R820TTunerController controller =
|
||||
new R820TTunerController( device, deviceDescriptor,
|
||||
mThreadPoolManager );
|
||||
|
||||
controller.init();
|
||||
|
||||
RTL2832Tuner rtlTuner =
|
||||
new RTL2832Tuner( tunerClass, controller );
|
||||
|
||||
return new TunerInitStatus( rtlTuner, "LOADED" );
|
||||
}
|
||||
catch( SourceException se )
|
||||
{
|
||||
mLog.error( "error constructing tuner", se );
|
||||
|
||||
return new TunerInitStatus( null, "Error constructing R820T "
|
||||
+ "tuner controller - " + se.getLocalizedMessage() );
|
||||
}
|
||||
case FITIPOWER_FC0012:
|
||||
case FITIPOWER_FC0013:
|
||||
case RAFAELMICRO_R828D:
|
||||
case UNKNOWN:
|
||||
default:
|
||||
reason = "SDRTRunk doesn't currently support RTL2832 "
|
||||
+ "Dongle with [" + tunerType.toString() +
|
||||
"] tuner for tuner class[" + tunerClass.toString() + "]";
|
||||
break;
|
||||
}
|
||||
|
||||
return new TunerInitStatus( null, reason );
|
||||
}
|
||||
return new TunerInitStatus(null, "Unknown Device");
|
||||
}
|
||||
|
||||
private TunerInitStatus initAirspyTuner(Device device,
|
||||
DeviceDescriptor descriptor)
|
||||
{
|
||||
try
|
||||
{
|
||||
AirspyTunerController airspyController = new AirspyTunerController(device);
|
||||
|
||||
airspyController.init();
|
||||
|
||||
AirspyTuner tuner = new AirspyTuner(airspyController);
|
||||
|
||||
return new TunerInitStatus(tuner, "LOADED");
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("couldn't construct Airspy controller/tuner", se);
|
||||
|
||||
return new TunerInitStatus(null,
|
||||
"error constructing Airspy tuner controller");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private TunerInitStatus initEttusB100Tuner(Device device,
|
||||
DeviceDescriptor descriptor)
|
||||
{
|
||||
return new TunerInitStatus(null, "Ettus B100 tuner not currently "
|
||||
+ "supported");
|
||||
}
|
||||
|
||||
private TunerInitStatus initFuncubeProTuner(Device device,
|
||||
DeviceDescriptor descriptor)
|
||||
{
|
||||
String reason = "NOT LOADED";
|
||||
|
||||
MixerTunerDataLine dataline = getMixerTunerDataLine(
|
||||
TunerClass.FUNCUBE_DONGLE_PRO.getTunerType());
|
||||
|
||||
if(dataline != null)
|
||||
{
|
||||
FCD1TunerController controller =
|
||||
new FCD1TunerController(device, descriptor);
|
||||
|
||||
try
|
||||
{
|
||||
controller.init();
|
||||
|
||||
FCDTuner tuner =
|
||||
new FCDTuner(dataline, controller);
|
||||
|
||||
return new TunerInitStatus(tuner, "LOADED");
|
||||
}
|
||||
catch(SourceException e)
|
||||
{
|
||||
mLog.error("couldn't load funcube dongle pro tuner", e);
|
||||
|
||||
reason = "error during initialization - " + e.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reason = "couldn't find matching mixer dataline";
|
||||
}
|
||||
|
||||
return new TunerInitStatus(null, "Funcube Dongle Pro tuner not "
|
||||
+ "loaded - " + reason);
|
||||
}
|
||||
|
||||
private TunerInitStatus initFuncubeProPlusTuner(Device device,
|
||||
DeviceDescriptor descriptor)
|
||||
{
|
||||
String reason = "NOT LOADED";
|
||||
|
||||
MixerTunerDataLine dataline = getMixerTunerDataLine(
|
||||
TunerClass.FUNCUBE_DONGLE_PRO_PLUS.getTunerType());
|
||||
|
||||
if(dataline != null)
|
||||
{
|
||||
FCD2TunerController controller =
|
||||
new FCD2TunerController(device, descriptor);
|
||||
|
||||
try
|
||||
{
|
||||
controller.init();
|
||||
|
||||
FCDTuner tuner =
|
||||
new FCDTuner(dataline, controller);
|
||||
|
||||
return new TunerInitStatus(tuner, "LOADED");
|
||||
}
|
||||
catch(SourceException e)
|
||||
{
|
||||
mLog.error("couldn't load funcube dongle pro plus tuner", e);
|
||||
|
||||
reason = "error during initialization - " +
|
||||
e.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reason = "couldn't find matching mixer dataline";
|
||||
}
|
||||
|
||||
return new TunerInitStatus(null, "Funcube Dongle Pro tuner not "
|
||||
+ "loaded - " + reason);
|
||||
}
|
||||
|
||||
private TunerInitStatus initHackRFTuner(Device device,
|
||||
DeviceDescriptor descriptor)
|
||||
{
|
||||
try
|
||||
{
|
||||
HackRFTunerController hackRFController = new HackRFTunerController(device, descriptor);
|
||||
|
||||
hackRFController.init();
|
||||
|
||||
HackRFTuner tuner = new HackRFTuner(hackRFController);
|
||||
|
||||
return new TunerInitStatus(tuner, "LOADED");
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("couldn't construct HackRF controller/tuner", se);
|
||||
|
||||
return new TunerInitStatus(null,
|
||||
"error constructing HackRF tuner controller");
|
||||
}
|
||||
}
|
||||
|
||||
private TunerInitStatus initRTL2832Tuner(TunerClass tunerClass,
|
||||
Device device,
|
||||
DeviceDescriptor deviceDescriptor)
|
||||
{
|
||||
String reason = "NOT LOADED";
|
||||
|
||||
TunerType tunerType = tunerClass.getTunerType();
|
||||
|
||||
if(tunerType == TunerType.RTL2832_VARIOUS)
|
||||
{
|
||||
try
|
||||
{
|
||||
tunerType = RTL2832TunerController.identifyTunerType(device);
|
||||
}
|
||||
catch(SourceException e)
|
||||
{
|
||||
mLog.error("couldn't determine RTL2832 tuner type", e);
|
||||
tunerType = TunerType.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
switch(tunerType)
|
||||
{
|
||||
case ELONICS_E4000:
|
||||
try
|
||||
{
|
||||
E4KTunerController controller = new E4KTunerController(device, deviceDescriptor);
|
||||
|
||||
controller.init();
|
||||
|
||||
RTL2832Tuner rtlTuner = new RTL2832Tuner(tunerClass, controller);
|
||||
|
||||
return new TunerInitStatus(rtlTuner, "LOADED");
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
return new TunerInitStatus(null, "Error constructing E4K tuner controller - " +
|
||||
se.getLocalizedMessage());
|
||||
}
|
||||
case RAFAELMICRO_R820T:
|
||||
try
|
||||
{
|
||||
R820TTunerController controller = new R820TTunerController(device, deviceDescriptor);
|
||||
|
||||
controller.init();
|
||||
|
||||
RTL2832Tuner rtlTuner = new RTL2832Tuner(tunerClass, controller);
|
||||
|
||||
return new TunerInitStatus(rtlTuner, "LOADED");
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("error constructing tuner", se);
|
||||
|
||||
return new TunerInitStatus(null, "Error constructing R820T "
|
||||
+ "tuner controller - " + se.getLocalizedMessage());
|
||||
}
|
||||
case FITIPOWER_FC0012:
|
||||
case FITIPOWER_FC0013:
|
||||
case RAFAELMICRO_R828D:
|
||||
case UNKNOWN:
|
||||
default:
|
||||
reason = "SDRTRunk doesn't currently support RTL2832 "
|
||||
+ "Dongle with [" + tunerType.toString() +
|
||||
"] tuner for tuner class[" + tunerClass.toString() + "]";
|
||||
break;
|
||||
}
|
||||
|
||||
return new TunerInitStatus(null, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first tuner mixer dataline that corresponds to the tuner class.
|
||||
*
|
||||
*
|
||||
* Note: this method is not currently able to align multiple tuner mixer
|
||||
* data lines of the same tuner type. If you have multiple Funcube Dongle
|
||||
* tuners of the same TYPE, there is no guarantee that you will get the
|
||||
* tuners of the same TYPE, there is no guarantee that you will get the
|
||||
* correct mixer.
|
||||
*
|
||||
*
|
||||
* @param tunerClass
|
||||
* @return
|
||||
*/
|
||||
private MixerTunerDataLine getMixerTunerDataLine( TunerType tunerClass )
|
||||
private MixerTunerDataLine getMixerTunerDataLine(TunerType tunerClass)
|
||||
{
|
||||
Collection<MixerTunerDataLine> datalines =
|
||||
mMixerManager.getMixerTunerDataLines();
|
||||
Collection<MixerTunerDataLine> datalines =
|
||||
mMixerManager.getMixerTunerDataLines();
|
||||
|
||||
for( MixerTunerDataLine mixerTDL: datalines )
|
||||
{
|
||||
if( mixerTDL.getMixerTunerType().getTunerClass() == tunerClass )
|
||||
{
|
||||
return mixerTDL;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
for(MixerTunerDataLine mixerTDL : datalines)
|
||||
{
|
||||
if(mixerTDL.getMixerTunerType().getTunerClass() == tunerClass)
|
||||
{
|
||||
return mixerTDL;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public class TunerInitStatus
|
||||
{
|
||||
private Tuner mTuner;
|
||||
private String mInfo;
|
||||
|
||||
public TunerInitStatus( Tuner tuner, String info )
|
||||
{
|
||||
mTuner = tuner;
|
||||
mInfo = info;
|
||||
}
|
||||
|
||||
public Tuner getTuner()
|
||||
{
|
||||
return mTuner;
|
||||
}
|
||||
|
||||
public String getInfo()
|
||||
{
|
||||
return mInfo;
|
||||
}
|
||||
|
||||
public boolean isLoaded()
|
||||
{
|
||||
return mTuner != null;
|
||||
}
|
||||
private Tuner mTuner;
|
||||
private String mInfo;
|
||||
|
||||
public TunerInitStatus(Tuner tuner, String info)
|
||||
{
|
||||
mTuner = tuner;
|
||||
mInfo = info;
|
||||
}
|
||||
|
||||
public Tuner getTuner()
|
||||
{
|
||||
return mTuner;
|
||||
}
|
||||
|
||||
public String getInfo()
|
||||
{
|
||||
return mInfo;
|
||||
}
|
||||
|
||||
public boolean isLoaded()
|
||||
{
|
||||
return mTuner != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,6 @@
|
||||
******************************************************************************/
|
||||
package spectrum;
|
||||
|
||||
import controller.NamingThreadFactory;
|
||||
import dsp.filter.Window;
|
||||
import dsp.filter.Window.WindowType;
|
||||
import org.jtransforms.fft.FloatFFT_1D;
|
||||
@@ -32,14 +31,14 @@ import sample.complex.ComplexBuffer;
|
||||
import source.tuner.frequency.FrequencyChangeEvent;
|
||||
import source.tuner.frequency.IFrequencyChangeProcessor;
|
||||
import spectrum.converter.DFTResultsConverter;
|
||||
import util.ThreadPool;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@@ -60,8 +59,7 @@ public class DFTProcessor implements Listener<ComplexBuffer>, IFrequencyChangePr
|
||||
//so we set it at 12.
|
||||
private BlockingQueue<ComplexBuffer> mQueue = new ArrayBlockingQueue<>(12);
|
||||
|
||||
private ScheduledExecutorService mScheduler = Executors
|
||||
.newScheduledThreadPool(1, new NamingThreadFactory("spectrum dft processor"));
|
||||
private ScheduledFuture<?> mProcessorTaskHandle;
|
||||
|
||||
public static final String FRAME_RATE_PROPERTY = "spectral.display.frame.rate";
|
||||
|
||||
@@ -184,38 +182,20 @@ public class DFTProcessor implements Listener<ComplexBuffer>, IFrequencyChangePr
|
||||
|
||||
public void start()
|
||||
{
|
||||
/**
|
||||
* Reset the scheduler
|
||||
*/
|
||||
mScheduler = Executors.newScheduledThreadPool(1, new NamingThreadFactory("spectrum dft"));
|
||||
|
||||
/**
|
||||
* Schedule the DFT to run calculations at a fixed rate
|
||||
*/
|
||||
//Schedule the DFT to run calculations at a fixed rate
|
||||
int initialDelay = 0;
|
||||
int period = (int) (1000 / mFrameRate);
|
||||
TimeUnit unit = TimeUnit.MILLISECONDS;
|
||||
|
||||
mScheduler.scheduleAtFixedRate(new DFTCalculationTask(),
|
||||
initialDelay, period, unit);
|
||||
mProcessorTaskHandle = ThreadPool.SCHEDULED.scheduleAtFixedRate(new DFTCalculationTask(), initialDelay, period,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
/**
|
||||
* Shutdown the scheduler and clear out any remaining tasks
|
||||
*/
|
||||
try
|
||||
//Cancel running DFT calculation task
|
||||
if(mProcessorTaskHandle != null)
|
||||
{
|
||||
mScheduler.shutdown();
|
||||
|
||||
mScheduler.awaitTermination(100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch(InterruptedException e)
|
||||
{
|
||||
/* Do nothing ... we're shutting down */
|
||||
// mLog.error( "DFTProcessor - exception while awaiting shutdown of "
|
||||
// + "calculation scheduler for reset", e );
|
||||
mProcessorTaskHandle.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
54
src/util/ThreadPool.java
Normal file
54
src/util/ThreadPool.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package util;
|
||||
|
||||
import controller.NamingThreadFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
public class ThreadPool
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ThreadPool.class);
|
||||
|
||||
private static int CORES = Runtime.getRuntime().availableProcessors();
|
||||
public static ScheduledExecutorService SCHEDULED;
|
||||
|
||||
static
|
||||
{
|
||||
//Create a scheduled thread pool sized according to the available processors/cores, minimum 2
|
||||
CORES = (CORES < 2 ? 2 : CORES);
|
||||
|
||||
SCHEDULED = Executors.newScheduledThreadPool(CORES, new NamingThreadFactory("sdrtrunk"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Application-wide shared thread pools and scheduled executor service.
|
||||
*/
|
||||
public ThreadPool()
|
||||
{
|
||||
}
|
||||
|
||||
public static void logSettings()
|
||||
{
|
||||
mLog.info("Application thread pool created with [" + CORES + "] threads");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user