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