mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 17:17:29 -05:00
Compare commits
16 Commits
v0.3.0-bet
...
v0.3.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
980ad72d4d | ||
|
|
9b746cc032 | ||
|
|
f51ae0b5da | ||
|
|
561078e9bf | ||
|
|
e052b01f9b | ||
|
|
09cf4fcfd7 | ||
|
|
5e731d0e9f | ||
|
|
670a02b0ba | ||
|
|
9ef156b061 | ||
|
|
d9cf1b19f3 | ||
|
|
a4a339b505 | ||
|
|
119370c46e | ||
|
|
4eb66e6045 | ||
|
|
8827ecd0e9 | ||
|
|
8660002f93 | ||
|
|
2058fcbc1d |
16
README.md
16
README.md
@@ -2,18 +2,10 @@
|
|||||||
A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR).
|
A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR).
|
||||||
|
|
||||||
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted)
|
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted)
|
||||||
* [User's Manual](https://github.com/DSheirer/sdrtrunk/wiki/UserManual)
|
* [User's Manual Version 0.2.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual)
|
||||||
|
* [User's Manual Version 0.3.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual_Version0.3.0)
|
||||||
* [Download](https://github.com/DSheirer/sdrtrunk/releases)
|
* [Download](https://github.com/DSheirer/sdrtrunk/releases)
|
||||||
* [Support](https://groups.google.com/forum/#!forum/sdrtrunk)
|
* [Support](https://groups.google.com/forum/#!forum/sdrtrunk)
|
||||||
|
|
||||||

|

|
||||||
<script>
|
**Figure 1:** sdrtrunk **Version 0.3.0** Application Screenshot
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
||||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
||||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
|
||||||
|
|
||||||
ga('create', 'UA-67489234-1', 'auto');
|
|
||||||
ga('send', 'pageview');
|
|
||||||
|
|
||||||
</script>
|
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
<property name="major" value="0"/>
|
<property name="major" value="0"/>
|
||||||
<property name="minor" value="3"/>
|
<property name="minor" value="3"/>
|
||||||
<property name="patch" value="0"/>
|
<property name="patch" value="0"/>
|
||||||
<property name="release" value="-beta5"/>
|
<property name="release" value="-beta7"/>
|
||||||
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}${release}"/>
|
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}${release}"/>
|
||||||
|
|
||||||
<!-- Build properties -->
|
<!-- Build properties -->
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public class AliasModel extends AbstractTableModel
|
|||||||
{
|
{
|
||||||
for(Alias alias : mAliases)
|
for(Alias alias : mAliases)
|
||||||
{
|
{
|
||||||
if(alias.getList().equalsIgnoreCase(name))
|
if(alias.hasList() && alias.getList().equalsIgnoreCase(name))
|
||||||
{
|
{
|
||||||
aliasList.addAlias(alias);
|
aliasList.addAlias(alias);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,10 +40,11 @@ public class AliasNameEditor extends Editor<Alias>
|
|||||||
{
|
{
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private static ComboBoxModel<String> EMPTY_MODEL = new DefaultComboBoxModel<>();
|
private static ComboBoxModel<String> EMPTY_LIST_MODEL = new DefaultComboBoxModel<>();
|
||||||
|
private static ComboBoxModel<String> EMPTY_GROUP_MODEL = new DefaultComboBoxModel<>();
|
||||||
|
|
||||||
private JComboBox<String> mListCombo = new JComboBox<>(EMPTY_MODEL);
|
private JComboBox<String> mListCombo = new JComboBox<>(EMPTY_LIST_MODEL);
|
||||||
private JComboBox<String> mGroupCombo = new JComboBox<>(EMPTY_MODEL);
|
private JComboBox<String> mGroupCombo = new JComboBox<>(EMPTY_GROUP_MODEL);
|
||||||
private JComboBox<Icon> mIconCombo;
|
private JComboBox<Icon> mIconCombo;
|
||||||
private JTextField mName;
|
private JTextField mName;
|
||||||
private JButton mButtonColor;
|
private JButton mButtonColor;
|
||||||
@@ -110,7 +111,7 @@ public class AliasNameEditor extends Editor<Alias>
|
|||||||
|
|
||||||
if(groups.isEmpty())
|
if(groups.isEmpty())
|
||||||
{
|
{
|
||||||
mGroupCombo.setModel(EMPTY_MODEL);
|
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -238,7 +239,7 @@ public class AliasNameEditor extends Editor<Alias>
|
|||||||
|
|
||||||
if(listNames.isEmpty())
|
if(listNames.isEmpty())
|
||||||
{
|
{
|
||||||
mListCombo.setModel(EMPTY_MODEL);
|
mListCombo.setModel(EMPTY_LIST_MODEL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -253,7 +254,7 @@ public class AliasNameEditor extends Editor<Alias>
|
|||||||
|
|
||||||
if(groupNames.isEmpty())
|
if(groupNames.isEmpty())
|
||||||
{
|
{
|
||||||
mGroupCombo.setModel(EMPTY_MODEL);
|
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -285,8 +286,8 @@ public class AliasNameEditor extends Editor<Alias>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mListCombo.setModel(EMPTY_MODEL);
|
mListCombo.setModel(EMPTY_LIST_MODEL);
|
||||||
mGroupCombo.setModel(EMPTY_MODEL);
|
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
|
||||||
mName.setText(null);
|
mName.setText(null);
|
||||||
|
|
||||||
mButtonColor.setBackground(getBackground());
|
mButtonColor.setBackground(getBackground());
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package alias.id;
|
package alias.id;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class WildcardID implements Comparable<WildcardID>
|
public class WildcardID implements Comparable<WildcardID>
|
||||||
@@ -45,7 +47,7 @@ public class WildcardID implements Comparable<WildcardID>
|
|||||||
*/
|
*/
|
||||||
public WildcardID(String value)
|
public WildcardID(String value)
|
||||||
{
|
{
|
||||||
assert(value != null && value.contains(WILDCARD) && value.length() < 10);
|
Validate.isTrue(value != null && value.contains(WILDCARD) && value.length() < 10);
|
||||||
|
|
||||||
mValue = value;
|
mValue = value;
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ package audio.broadcast.shoutcast.v2;
|
|||||||
import audio.broadcast.BroadcastConfiguration;
|
import audio.broadcast.BroadcastConfiguration;
|
||||||
import audio.broadcast.BroadcastFormat;
|
import audio.broadcast.BroadcastFormat;
|
||||||
import audio.broadcast.BroadcastServerType;
|
import audio.broadcast.BroadcastServerType;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAttribute;
|
import javax.xml.bind.annotation.XmlAttribute;
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ public class ShoutcastV2Configuration extends BroadcastConfiguration
|
|||||||
*/
|
*/
|
||||||
public void setStreamID(int streamID)
|
public void setStreamID(int streamID)
|
||||||
{
|
{
|
||||||
assert(1 <= streamID && streamID <= Integer.MAX_VALUE);
|
Validate.isTrue(1 <= streamID && streamID <= Integer.MAX_VALUE);
|
||||||
mStreamID = streamID;
|
mStreamID = streamID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.ultravox;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
public class NegotiateMaxPayloadSize extends UltravoxMessage
|
public class NegotiateMaxPayloadSize extends UltravoxMessage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -47,8 +49,8 @@ public class NegotiateMaxPayloadSize extends UltravoxMessage
|
|||||||
*/
|
*/
|
||||||
public void setMaximumPayloadSize(int desiredMaximum, int minimum)
|
public void setMaximumPayloadSize(int desiredMaximum, int minimum)
|
||||||
{
|
{
|
||||||
assert(desiredMaximum <= 16377);
|
Validate.isTrue(desiredMaximum <= 16377);
|
||||||
assert(minimum <= 16377);
|
Validate.isTrue(minimum <= 16377);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(desiredMaximum).append(":").append(minimum);
|
sb.append(desiredMaximum).append(":").append(minimum);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
package audio.broadcast.shoutcast.v2.ultravox;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
import bits.BinaryMessage;
|
import bits.BinaryMessage;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -154,7 +155,7 @@ public abstract class UltravoxMessage
|
|||||||
*/
|
*/
|
||||||
public void setSendQueuePriority(int priority)
|
public void setSendQueuePriority(int priority)
|
||||||
{
|
{
|
||||||
assert(0 <= priority && priority <= 7);
|
Validate.isTrue(0 <= priority && priority <= 7);
|
||||||
|
|
||||||
mMessage.setInt(priority, SEND_QUEUE_PRIORITY);
|
mMessage.setInt(priority, SEND_QUEUE_PRIORITY);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import java.util.BitSet;
|
|||||||
import java.util.logging.LogManager;
|
import java.util.logging.LogManager;
|
||||||
|
|
||||||
import edac.CRC;
|
import edac.CRC;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
public class BinaryMessage extends BitSet
|
public class BinaryMessage extends BitSet
|
||||||
{
|
{
|
||||||
@@ -456,7 +457,7 @@ public class BinaryMessage extends BitSet
|
|||||||
*/
|
*/
|
||||||
public byte getByte(int index)
|
public byte getByte(int index)
|
||||||
{
|
{
|
||||||
assert((index + 7) <= size());
|
Validate.isTrue((index + 7) <= size());
|
||||||
|
|
||||||
int value = 0;
|
int value = 0;
|
||||||
|
|
||||||
@@ -481,7 +482,7 @@ public class BinaryMessage extends BitSet
|
|||||||
*/
|
*/
|
||||||
public void setByte(int index, byte value)
|
public void setByte(int index, byte value)
|
||||||
{
|
{
|
||||||
assert((index + 8) <= size());
|
Validate.isTrue((index + 8) <= size());
|
||||||
|
|
||||||
int mask = 0x80;
|
int mask = 0x80;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package bits;
|
package bits;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -39,7 +41,7 @@ public class MultiSyncPatternMatcher
|
|||||||
|
|
||||||
public MultiSyncPatternMatcher( int syncSize )
|
public MultiSyncPatternMatcher( int syncSize )
|
||||||
{
|
{
|
||||||
assert( syncSize < 64 );
|
Validate.isTrue(syncSize < 64);
|
||||||
|
|
||||||
//Setup a bit mask of all ones the length of the sync pattern
|
//Setup a bit mask of all ones the length of the sync pattern
|
||||||
mMask = (long)( ( Math.pow( 2, syncSize ) ) - 1 );
|
mMask = (long)( ( Math.pow( 2, syncSize ) ) - 1 );
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import controller.channel.ChannelUtils;
|
|||||||
import icon.IconManager;
|
import icon.IconManager;
|
||||||
import module.ProcessingChain;
|
import module.ProcessingChain;
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import properties.SystemProperties;
|
import properties.SystemProperties;
|
||||||
@@ -140,7 +141,7 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
|
|||||||
|
|
||||||
public AliasedValueCellRenderer(Attribute attribute)
|
public AliasedValueCellRenderer(Attribute attribute)
|
||||||
{
|
{
|
||||||
assert(attribute != null);
|
Validate.isTrue(attribute != null);
|
||||||
mAttribute = attribute;
|
mAttribute = attribute;
|
||||||
setHorizontalAlignment(JLabel.CENTER);
|
setHorizontalAlignment(JLabel.CENTER);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,269 +1,267 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* 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 controller.channel;
|
package controller.channel;
|
||||||
|
|
||||||
|
import alias.AliasModel;
|
||||||
import gui.editor.Editor;
|
import gui.editor.Editor;
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.ComboBoxModel;
|
|
||||||
import javax.swing.DefaultComboBoxModel;
|
|
||||||
import javax.swing.JComboBox;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.event.DocumentEvent;
|
|
||||||
import javax.swing.event.DocumentListener;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import alias.AliasModel;
|
|
||||||
|
|
||||||
public class NameConfigurationEditor extends Editor<Channel> implements DocumentListener
|
public class NameConfigurationEditor extends Editor<Channel> implements DocumentListener
|
||||||
{
|
{
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( NameConfigurationEditor.class );
|
private final static Logger mLog = LoggerFactory.getLogger(NameConfigurationEditor.class);
|
||||||
|
|
||||||
private static ComboBoxModel<String> EMPTY_MODEL = new DefaultComboBoxModel<>();
|
private static ComboBoxModel<String> EMPTY_SYSTEM_MODEL = new DefaultComboBoxModel<>();
|
||||||
|
private static ComboBoxModel<String> EMPTY_SITE_MODEL = new DefaultComboBoxModel<>();
|
||||||
|
private static ComboBoxModel<String> EMPTY_ALIAS_LIST_MODEL = new DefaultComboBoxModel<>();
|
||||||
|
|
||||||
private AliasModel mAliasModel;
|
private AliasModel mAliasModel;
|
||||||
private ChannelModel mChannelModel;
|
private ChannelModel mChannelModel;
|
||||||
|
|
||||||
private JTextField mChannelName;
|
|
||||||
private JComboBox<String> mSystemNameCombo;
|
|
||||||
private JComboBox<String> mSiteNameCombo;
|
|
||||||
private JComboBox<String> mAliasListCombo;
|
|
||||||
|
|
||||||
public NameConfigurationEditor( AliasModel aliasModel, ChannelModel model )
|
|
||||||
{
|
|
||||||
mAliasModel = aliasModel;
|
|
||||||
mChannelModel = model;
|
|
||||||
|
|
||||||
init();
|
private JTextField mChannelName;
|
||||||
}
|
private JComboBox<String> mSystemNameCombo;
|
||||||
|
private JComboBox<String> mSiteNameCombo;
|
||||||
private void init()
|
private JComboBox<String> mAliasListCombo;
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", "[][][grow]" ) );
|
|
||||||
|
|
||||||
mChannelName = new JTextField( "" );
|
public NameConfigurationEditor(AliasModel aliasModel, ChannelModel model)
|
||||||
mChannelName.getDocument().addDocumentListener( this );
|
{
|
||||||
mChannelName.setEnabled( false );
|
mAliasModel = aliasModel;
|
||||||
|
mChannelModel = model;
|
||||||
|
|
||||||
add( new JLabel( "Name:" ) );
|
init();
|
||||||
add( mChannelName );
|
}
|
||||||
|
|
||||||
mSystemNameCombo = new JComboBox<>( EMPTY_MODEL );
|
private void init()
|
||||||
mSystemNameCombo.setEnabled( false );
|
{
|
||||||
mSystemNameCombo.setEditable( true );
|
setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]", "[][][grow]"));
|
||||||
mSystemNameCombo.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
Object selected = mSystemNameCombo.getSelectedItem();
|
|
||||||
|
|
||||||
if( selected != null )
|
|
||||||
{
|
|
||||||
String system = (String)selected;
|
|
||||||
|
|
||||||
List<String> sites = mChannelModel.getSites( system );
|
|
||||||
|
|
||||||
if( !sites.contains( getItem().getSite() ) )
|
mChannelName = new JTextField("");
|
||||||
{
|
mChannelName.getDocument().addDocumentListener(this);
|
||||||
sites.add( getItem().getSite() );
|
mChannelName.setEnabled(false);
|
||||||
}
|
|
||||||
|
|
||||||
if( sites.isEmpty() )
|
|
||||||
{
|
|
||||||
mSiteNameCombo.setModel( EMPTY_MODEL );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mSiteNameCombo.setModel( new DefaultComboBoxModel<String>(
|
|
||||||
sites.toArray( new String[ sites.size() ] ) ) );;
|
|
||||||
}
|
|
||||||
|
|
||||||
mSiteNameCombo.setSelectedItem( getItem().getSite() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
add( new JLabel( "System:" ) );
|
|
||||||
add( mSystemNameCombo );
|
|
||||||
|
|
||||||
mAliasListCombo = new JComboBox<String>();
|
add(new JLabel("Name:"));
|
||||||
mAliasListCombo.setEnabled( false );
|
add(mChannelName);
|
||||||
mAliasListCombo.setEditable( true );
|
|
||||||
mAliasListCombo.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
add( new JLabel( "Alias List:" ) );
|
|
||||||
add( mAliasListCombo );
|
|
||||||
|
|
||||||
mSiteNameCombo = new JComboBox<>( EMPTY_MODEL );
|
|
||||||
mSiteNameCombo.setEnabled( false );
|
|
||||||
mSiteNameCombo.setEditable( true );
|
|
||||||
mSiteNameCombo.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
add( new JLabel( "Site:" ) );
|
|
||||||
add( mSiteNameCombo );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
if( hasItem() && isModified() )
|
|
||||||
{
|
|
||||||
getItem().setName( mChannelName.getText() );
|
|
||||||
|
|
||||||
Object system = mSystemNameCombo.getSelectedItem();
|
|
||||||
getItem().setSystem( ( system == null ? null : (String)system ) );
|
|
||||||
|
|
||||||
Object site = mSiteNameCombo.getSelectedItem();
|
mSystemNameCombo = new JComboBox<>(EMPTY_SYSTEM_MODEL);
|
||||||
getItem().setSite( ( site == null ? null : (String)site ) );
|
mSystemNameCombo.setEnabled(false);
|
||||||
|
mSystemNameCombo.setEditable(true);
|
||||||
|
mSystemNameCombo.addActionListener(new ActionListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e)
|
||||||
|
{
|
||||||
|
Object selected = mSystemNameCombo.getSelectedItem();
|
||||||
|
|
||||||
Object aliasList = mAliasListCombo.getSelectedItem();
|
if(selected != null)
|
||||||
getItem().setAliasListName( aliasList == null ? null : (String)aliasList );
|
{
|
||||||
}
|
String system = (String)selected;
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setControlsEnabled( boolean enabled )
|
List<String> sites = mChannelModel.getSites(system);
|
||||||
{
|
|
||||||
if( mChannelName.isEnabled() != enabled )
|
|
||||||
{
|
|
||||||
mChannelName.setEnabled( enabled );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( mAliasListCombo.isEnabled() != enabled )
|
if(!sites.contains(getItem().getSite()))
|
||||||
{
|
{
|
||||||
mAliasListCombo.setEnabled( enabled );
|
sites.add(getItem().getSite());
|
||||||
}
|
}
|
||||||
|
|
||||||
if( mSystemNameCombo.isEnabled() != enabled )
|
if(sites.isEmpty())
|
||||||
{
|
{
|
||||||
mSystemNameCombo.setEnabled( enabled );
|
mSiteNameCombo.setModel(EMPTY_SITE_MODEL);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if( mSiteNameCombo.isEnabled() != enabled )
|
{
|
||||||
{
|
mSiteNameCombo.setModel(new DefaultComboBoxModel<String>(
|
||||||
mSiteNameCombo.setEnabled( enabled );
|
sites.toArray(new String[sites.size()])));
|
||||||
}
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
mSiteNameCombo.setSelectedItem(getItem().getSite());
|
||||||
public void setItem( Channel channel )
|
}
|
||||||
{
|
|
||||||
super.setItem( channel );
|
|
||||||
|
|
||||||
if( hasItem() )
|
|
||||||
{
|
|
||||||
setControlsEnabled( true );
|
|
||||||
|
|
||||||
mChannelName.setText( channel.getName() );
|
|
||||||
|
|
||||||
List<String> systems = mChannelModel.getSystems();
|
setModified(true);
|
||||||
|
}
|
||||||
if( systems.isEmpty() )
|
});
|
||||||
{
|
|
||||||
mSystemNameCombo.setModel( EMPTY_MODEL );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mSystemNameCombo.setModel( new DefaultComboBoxModel<String>(
|
|
||||||
systems.toArray( new String[ systems.size() ] ) ) );;
|
|
||||||
}
|
|
||||||
|
|
||||||
mSystemNameCombo.setSelectedItem( channel.getSystem() );
|
|
||||||
|
|
||||||
List<String> sites = mChannelModel.getSites( channel.getSystem() );
|
add(new JLabel("System:"));
|
||||||
|
add(mSystemNameCombo);
|
||||||
|
|
||||||
if( sites.isEmpty() )
|
mAliasListCombo = new JComboBox<String>(EMPTY_ALIAS_LIST_MODEL);
|
||||||
{
|
mAliasListCombo.setEnabled(false);
|
||||||
mSiteNameCombo.setModel( EMPTY_MODEL );
|
mAliasListCombo.setEditable(true);
|
||||||
}
|
mAliasListCombo.addActionListener(new ActionListener()
|
||||||
else
|
{
|
||||||
{
|
@Override
|
||||||
mSiteNameCombo.setModel( new DefaultComboBoxModel<String>(
|
public void actionPerformed(ActionEvent e)
|
||||||
sites.toArray( new String[ sites.size() ] ) ) );;
|
{
|
||||||
}
|
setModified(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if( channel.getSite() != null )
|
add(new JLabel("Alias List:"));
|
||||||
{
|
add(mAliasListCombo);
|
||||||
mSiteNameCombo.setSelectedItem( channel.getSite() );
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> lists = mAliasModel.getListNames();
|
|
||||||
Collections.sort( lists );
|
|
||||||
mAliasListCombo.setModel( new DefaultComboBoxModel<String>(
|
|
||||||
lists.toArray( new String[ lists.size() ] ) ) );
|
|
||||||
|
|
||||||
if( channel.getAliasListName() != null )
|
|
||||||
{
|
|
||||||
mAliasListCombo.setSelectedItem( channel.getAliasListName() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
setControlsEnabled( false );
|
|
||||||
mChannelName.setText( null );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
mSiteNameCombo = new JComboBox<>(EMPTY_SITE_MODEL);
|
||||||
public void insertUpdate( DocumentEvent e )
|
mSiteNameCombo.setEnabled(false);
|
||||||
{
|
mSiteNameCombo.setEditable(true);
|
||||||
setModified( true );
|
mSiteNameCombo.addActionListener(new ActionListener()
|
||||||
}
|
{
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e)
|
||||||
|
{
|
||||||
|
setModified(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@Override
|
add(new JLabel("Site:"));
|
||||||
public void changedUpdate( DocumentEvent e )
|
add(mSiteNameCombo);
|
||||||
{
|
}
|
||||||
setModified( true );
|
|
||||||
}
|
@Override
|
||||||
|
public void save()
|
||||||
@Override
|
{
|
||||||
public void removeUpdate( DocumentEvent e )
|
if(hasItem() && isModified())
|
||||||
{
|
{
|
||||||
setModified( true );
|
getItem().setName(mChannelName.getText());
|
||||||
}
|
|
||||||
|
Object system = mSystemNameCombo.getSelectedItem();
|
||||||
|
getItem().setSystem((system == null ? null : (String)system));
|
||||||
|
|
||||||
|
Object site = mSiteNameCombo.getSelectedItem();
|
||||||
|
getItem().setSite((site == null ? null : (String)site));
|
||||||
|
|
||||||
|
Object aliasList = mAliasListCombo.getSelectedItem();
|
||||||
|
getItem().setAliasListName(aliasList == null ? null : (String)aliasList);
|
||||||
|
}
|
||||||
|
|
||||||
|
setModified(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setControlsEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
if(mChannelName.isEnabled() != enabled)
|
||||||
|
{
|
||||||
|
mChannelName.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mAliasListCombo.isEnabled() != enabled)
|
||||||
|
{
|
||||||
|
mAliasListCombo.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mSystemNameCombo.isEnabled() != enabled)
|
||||||
|
{
|
||||||
|
mSystemNameCombo.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mSiteNameCombo.isEnabled() != enabled)
|
||||||
|
{
|
||||||
|
mSiteNameCombo.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setItem(Channel channel)
|
||||||
|
{
|
||||||
|
super.setItem(channel);
|
||||||
|
|
||||||
|
if(hasItem())
|
||||||
|
{
|
||||||
|
setControlsEnabled(true);
|
||||||
|
|
||||||
|
mChannelName.setText(channel.getName());
|
||||||
|
|
||||||
|
List<String> systems = mChannelModel.getSystems();
|
||||||
|
|
||||||
|
if(systems.isEmpty())
|
||||||
|
{
|
||||||
|
mSystemNameCombo.setModel(EMPTY_SYSTEM_MODEL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mSystemNameCombo.setModel(new DefaultComboBoxModel<String>(
|
||||||
|
systems.toArray(new String[systems.size()])));
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
mSystemNameCombo.setSelectedItem(channel.getSystem());
|
||||||
|
|
||||||
|
List<String> sites = mChannelModel.getSites(channel.getSystem());
|
||||||
|
|
||||||
|
if(sites.isEmpty())
|
||||||
|
{
|
||||||
|
mSiteNameCombo.setModel(EMPTY_SITE_MODEL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mSiteNameCombo.setModel(new DefaultComboBoxModel<String>(
|
||||||
|
sites.toArray(new String[sites.size()])));
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(channel.getSite() != null)
|
||||||
|
{
|
||||||
|
mSiteNameCombo.setSelectedItem(channel.getSite());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> lists = mAliasModel.getListNames();
|
||||||
|
Collections.sort(lists);
|
||||||
|
mAliasListCombo.setModel(new DefaultComboBoxModel<String>(
|
||||||
|
lists.toArray(new String[lists.size()])));
|
||||||
|
|
||||||
|
if(channel.getAliasListName() != null)
|
||||||
|
{
|
||||||
|
mAliasListCombo.setSelectedItem(channel.getAliasListName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setControlsEnabled(false);
|
||||||
|
mChannelName.setText(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
setModified(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e)
|
||||||
|
{
|
||||||
|
setModified(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e)
|
||||||
|
{
|
||||||
|
setModified(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e)
|
||||||
|
{
|
||||||
|
setModified(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package dsp.filter;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import sample.real.RealSampleListener;
|
import sample.real.RealSampleListener;
|
||||||
|
|
||||||
public class FloatFIRFilter implements RealSampleListener
|
public class FloatFIRFilter implements RealSampleListener
|
||||||
@@ -139,7 +140,7 @@ public class FloatFIRFilter implements RealSampleListener
|
|||||||
private void generateIndexMap( int size )
|
private void generateIndexMap( int size )
|
||||||
{
|
{
|
||||||
//Ensure we have an odd size
|
//Ensure we have an odd size
|
||||||
assert( size % 2 == 1 );
|
Validate.isTrue(size % 2 == 1);
|
||||||
|
|
||||||
mIndexMap = new int[ size ][ size ];
|
mIndexMap = new int[ size ][ size ];
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package dsp.filter;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import sample.real.RealSampleListener;
|
import sample.real.RealSampleListener;
|
||||||
|
|
||||||
public class FloatHalfBandFilter implements RealSampleListener
|
public class FloatHalfBandFilter implements RealSampleListener
|
||||||
@@ -149,7 +150,7 @@ public class FloatHalfBandFilter implements RealSampleListener
|
|||||||
private void generateIndexMap( int size )
|
private void generateIndexMap( int size )
|
||||||
{
|
{
|
||||||
//Ensure we have an odd size
|
//Ensure we have an odd size
|
||||||
assert( size % 2 == 1 );
|
Validate.isTrue(size % 2 == 1);
|
||||||
|
|
||||||
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
|
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package dsp.filter;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import sample.real.RealSampleListener;
|
import sample.real.RealSampleListener;
|
||||||
|
|
||||||
public class FloatHalfBandNoDecimateFilter implements RealSampleListener
|
public class FloatHalfBandNoDecimateFilter implements RealSampleListener
|
||||||
@@ -139,7 +140,7 @@ public class FloatHalfBandNoDecimateFilter implements RealSampleListener
|
|||||||
private void generateIndexMap( int size )
|
private void generateIndexMap( int size )
|
||||||
{
|
{
|
||||||
//Ensure we have an odd size
|
//Ensure we have an odd size
|
||||||
assert( size % 2 == 1 );
|
Validate.isTrue(size % 2 == 1);
|
||||||
|
|
||||||
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
|
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package dsp.filter.cic;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import sample.Listener;
|
import sample.Listener;
|
||||||
import sample.complex.ComplexBuffer;
|
import sample.complex.ComplexBuffer;
|
||||||
import sample.complex.ComplexSampleListener;
|
import sample.complex.ComplexSampleListener;
|
||||||
@@ -67,7 +68,7 @@ public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
|
|||||||
public ComplexPrimeCICDecimate( int decimation, int order,
|
public ComplexPrimeCICDecimate( int decimation, int order,
|
||||||
int passFrequency, int attenuation, WindowType windowType )
|
int passFrequency, int attenuation, WindowType windowType )
|
||||||
{
|
{
|
||||||
assert( decimation <= 700 );
|
Validate.isTrue(decimation <= 700);
|
||||||
|
|
||||||
|
|
||||||
List<Integer> stageSizes = getPrimeFactors( decimation );
|
List<Integer> stageSizes = getPrimeFactors( decimation );
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package dsp.filter.cic;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -81,7 +82,7 @@ public class RealPrimeCICDecimate
|
|||||||
public RealPrimeCICDecimate( int decimation, int order, int passFrequency,
|
public RealPrimeCICDecimate( int decimation, int order, int passFrequency,
|
||||||
int attenuation, WindowType windowType, int outputBufferSize )
|
int attenuation, WindowType windowType, int outputBufferSize )
|
||||||
{
|
{
|
||||||
assert( decimation <= 700 );
|
Validate.isTrue(decimation <= 700);
|
||||||
|
|
||||||
mOutputBufferSize = outputBufferSize;
|
mOutputBufferSize = outputBufferSize;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package dsp.filter.halfband.real;
|
package dsp.filter.halfband.real;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
public class HalfBandFilter
|
public class HalfBandFilter
|
||||||
{
|
{
|
||||||
private float[] mBuffer;
|
private float[] mBuffer;
|
||||||
@@ -122,7 +124,7 @@ public class HalfBandFilter
|
|||||||
private void generateIndexMap( int size )
|
private void generateIndexMap( int size )
|
||||||
{
|
{
|
||||||
//Ensure we have an odd size
|
//Ensure we have an odd size
|
||||||
assert( size % 2 == 1 );
|
Validate.isTrue(size % 2 == 1);
|
||||||
|
|
||||||
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
|
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dsp.filter.interpolator;
|
package dsp.filter.interpolator;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ public class RealInterpolator extends Interpolator
|
|||||||
public float filter( float[] samples, int offset, float mu )
|
public float filter( float[] samples, int offset, float mu )
|
||||||
{
|
{
|
||||||
/* Ensure we have enough samples in the array */
|
/* Ensure we have enough samples in the array */
|
||||||
assert( samples.length >= offset + 7 );
|
Validate.isTrue(samples.length >= offset + 7);
|
||||||
|
|
||||||
/* Identify the filter bank that corresponds to mu */
|
/* Identify the filter bank that corresponds to mu */
|
||||||
int index = (int)( NSTEPS * mu );
|
int index = (int)( NSTEPS * mu );
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package dsp.filter.polyphase;
|
package dsp.filter.polyphase;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ public class PolyphaseFIRInterpolatingFilter
|
|||||||
public PolyphaseFIRInterpolatingFilter( double[] coefficients, int interpolation )
|
public PolyphaseFIRInterpolatingFilter( double[] coefficients, int interpolation )
|
||||||
{
|
{
|
||||||
/* Ensure coefficients are an integral multiple of the interpolation factor */
|
/* Ensure coefficients are an integral multiple of the interpolation factor */
|
||||||
assert( coefficients.length % interpolation == 0 );
|
Validate.isTrue(coefficients.length % interpolation == 0);
|
||||||
|
|
||||||
mTaps = coefficients;
|
mTaps = coefficients;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package dsp.gain;
|
package dsp.gain;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ public class DirectGainControl implements GainController, RealSampleListener
|
|||||||
*/
|
*/
|
||||||
public DirectGainControl( float gain, float minimum, float maximum, float increment )
|
public DirectGainControl( float gain, float minimum, float maximum, float increment )
|
||||||
{
|
{
|
||||||
assert( minimum > 0 );
|
Validate.isTrue(minimum > 0);
|
||||||
|
|
||||||
mDefaultGain = gain;
|
mDefaultGain = gain;
|
||||||
mGain = gain;
|
mGain = gain;
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ public class PlaylistManager implements ChannelEventListener
|
|||||||
@Override
|
@Override
|
||||||
public void receive(AliasEvent t)
|
public void receive(AliasEvent t)
|
||||||
{
|
{
|
||||||
|
//Save the playlist for all alias events
|
||||||
schedulePlaylistSave();
|
schedulePlaylistSave();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -98,6 +99,7 @@ public class PlaylistManager implements ChannelEventListener
|
|||||||
@Override
|
@Override
|
||||||
public void receive(ChannelMapEvent t)
|
public void receive(ChannelMapEvent t)
|
||||||
{
|
{
|
||||||
|
//Save the playlist for all channel map events
|
||||||
schedulePlaylistSave();
|
schedulePlaylistSave();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -107,7 +109,23 @@ public class PlaylistManager implements ChannelEventListener
|
|||||||
@Override
|
@Override
|
||||||
public void receive(BroadcastEvent broadcastEvent)
|
public void receive(BroadcastEvent broadcastEvent)
|
||||||
{
|
{
|
||||||
schedulePlaylistSave();
|
switch(broadcastEvent.getEvent())
|
||||||
|
{
|
||||||
|
case CONFIGURATION_ADD:
|
||||||
|
case CONFIGURATION_CHANGE:
|
||||||
|
case CONFIGURATION_DELETE:
|
||||||
|
schedulePlaylistSave();
|
||||||
|
break;
|
||||||
|
case BROADCASTER_ADD:
|
||||||
|
case BROADCASTER_QUEUE_CHANGE:
|
||||||
|
case BROADCASTER_STATE_CHANGE:
|
||||||
|
case BROADCASTER_STREAMED_COUNT_CHANGE:
|
||||||
|
case BROADCASTER_AGED_OFF_COUNT_CHANGE:
|
||||||
|
case BROADCASTER_DELETE:
|
||||||
|
default:
|
||||||
|
//Do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,79 +29,102 @@ import util.ThreadPool;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class RecorderManager implements Listener<AudioPacket>
|
public class RecorderManager implements Listener<AudioPacket>
|
||||||
{
|
{
|
||||||
private static final Logger mLog = LoggerFactory.getLogger(RecorderManager.class);
|
private static final Logger mLog = LoggerFactory.getLogger(RecorderManager.class);
|
||||||
|
|
||||||
public static final int AUDIO_SAMPLE_RATE = 8000;
|
public static final int AUDIO_SAMPLE_RATE = 8000;
|
||||||
|
public static final long IDLE_RECORDER_REMOVAL_THRESHOLD = 6000; //6 seconds
|
||||||
|
|
||||||
private Map<String,RealBufferWaveRecorder> mRecorders = new HashMap<>();
|
private Map<String,RealBufferWaveRecorder> mRecorders = new HashMap<>();
|
||||||
|
private ScheduledFuture<?> mRecorderMonitorFuture;
|
||||||
|
|
||||||
private boolean mCanStartNewRecorders = true;
|
private boolean mCanStartNewRecorders = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages all audio recording for all processing channels. Reconstructs audio streams and distributes channel
|
* Audio recording manager. Monitors stream of audio packets produced by decoding channels and automatically starts
|
||||||
* audio to each of the recorders, starting and stopping the recorders as needed.
|
* audio recorders when the channel's metadata designates a call as recordable. Routes call audio to each recorder
|
||||||
|
* based on audio packet metadata. Recorders are shutdown when the channel sends an end-call audio packet
|
||||||
|
* indicating that the call is complete. A separate recording monitor periodically checks for idled recorders to
|
||||||
|
* be stopped for cases when the channel fails to send an end-call audio packet.
|
||||||
*/
|
*/
|
||||||
public RecorderManager()
|
public RecorderManager()
|
||||||
{
|
{
|
||||||
|
mRecorderMonitorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(new RecorderMonitor(), 2,
|
||||||
|
2, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares this class for shutdown
|
||||||
|
*/
|
||||||
public void dispose()
|
public void dispose()
|
||||||
{
|
{
|
||||||
|
if(mRecorderMonitorFuture != null)
|
||||||
|
{
|
||||||
|
mRecorderMonitorFuture.cancel(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primary ingest point for audio packets from all decoding channels
|
||||||
|
* @param audioPacket to process
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void receive(AudioPacket audioPacket)
|
public void receive(AudioPacket audioPacket)
|
||||||
{
|
{
|
||||||
if(audioPacket.hasMetadata() &&
|
if(audioPacket.hasMetadata() && audioPacket.getMetadata().isRecordable())
|
||||||
audioPacket.getMetadata().isRecordable())
|
|
||||||
{
|
{
|
||||||
String identifier = audioPacket.getMetadata().getUniqueIdentifier();
|
String identifier = audioPacket.getMetadata().getUniqueIdentifier();
|
||||||
|
|
||||||
if(mRecorders.containsKey(identifier))
|
synchronized(mRecorders)
|
||||||
{
|
{
|
||||||
RealBufferWaveRecorder recorder = mRecorders.get(identifier);
|
if(mRecorders.containsKey(identifier))
|
||||||
|
|
||||||
if(audioPacket.getType() == AudioPacket.Type.AUDIO)
|
|
||||||
{
|
{
|
||||||
recorder.receive(audioPacket.getAudioBuffer());
|
RealBufferWaveRecorder recorder = mRecorders.get(identifier);
|
||||||
}
|
|
||||||
else if(audioPacket.getType() == AudioPacket.Type.END)
|
|
||||||
{
|
|
||||||
RealBufferWaveRecorder finished = mRecorders.remove(identifier);
|
|
||||||
finished.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(audioPacket.getType() == AudioPacket.Type.AUDIO)
|
|
||||||
{
|
|
||||||
if(mCanStartNewRecorders)
|
|
||||||
{
|
|
||||||
String filePrefix = getFilePrefix(audioPacket);
|
|
||||||
|
|
||||||
RealBufferWaveRecorder recorder = null;
|
if(audioPacket.getType() == AudioPacket.Type.AUDIO)
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
recorder = new RealBufferWaveRecorder(AUDIO_SAMPLE_RATE, filePrefix);
|
|
||||||
|
|
||||||
recorder.start(ThreadPool.SCHEDULED);
|
|
||||||
|
|
||||||
recorder.receive(audioPacket.getAudioBuffer());
|
recorder.receive(audioPacket.getAudioBuffer());
|
||||||
mRecorders.put(identifier, recorder);
|
|
||||||
}
|
}
|
||||||
catch(Exception ioe)
|
else if(audioPacket.getType() == AudioPacket.Type.END)
|
||||||
{
|
{
|
||||||
mCanStartNewRecorders = false;
|
RealBufferWaveRecorder finished = mRecorders.remove(identifier);
|
||||||
|
finished.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(audioPacket.getType() == AudioPacket.Type.AUDIO)
|
||||||
|
{
|
||||||
|
if(mCanStartNewRecorders)
|
||||||
|
{
|
||||||
|
String filePrefix = getFilePrefix(audioPacket);
|
||||||
|
|
||||||
mLog.error("Error attempting to start new audio wave recorder. All (future) audio recording " +
|
RealBufferWaveRecorder recorder = null;
|
||||||
"is disabled", ioe);
|
|
||||||
|
|
||||||
if(recorder != null)
|
try
|
||||||
{
|
{
|
||||||
recorder.stop();
|
recorder = new RealBufferWaveRecorder(AUDIO_SAMPLE_RATE, filePrefix);
|
||||||
|
|
||||||
|
recorder.start(ThreadPool.SCHEDULED);
|
||||||
|
|
||||||
|
recorder.receive(audioPacket.getAudioBuffer());
|
||||||
|
mRecorders.put(identifier, recorder);
|
||||||
|
}
|
||||||
|
catch(Exception ioe)
|
||||||
|
{
|
||||||
|
mCanStartNewRecorders = false;
|
||||||
|
|
||||||
|
mLog.error("Error attempting to start new audio wave recorder. All (future) audio recording " +
|
||||||
|
"is disabled", ioe);
|
||||||
|
|
||||||
|
if(recorder != null)
|
||||||
|
{
|
||||||
|
recorder.stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,4 +173,31 @@ public class RecorderManager implements Listener<AudioPacket>
|
|||||||
|
|
||||||
return new ComplexBufferWaveRecorder(AUDIO_SAMPLE_RATE, sb.toString());
|
return new ComplexBufferWaveRecorder(AUDIO_SAMPLE_RATE, sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Monitors currently running recorders and removes/closes any recorders that are idle more than 6 seconds
|
||||||
|
*/
|
||||||
|
public class RecorderMonitor implements Runnable
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
synchronized(mRecorders)
|
||||||
|
{
|
||||||
|
Iterator<Map.Entry<String,RealBufferWaveRecorder>> it = mRecorders.entrySet().iterator();
|
||||||
|
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
Map.Entry<String,RealBufferWaveRecorder> entry = it.next();
|
||||||
|
|
||||||
|
if(entry.getValue().getLastBufferReceived() + IDLE_RECORDER_REMOVAL_THRESHOLD < System.currentTimeMillis())
|
||||||
|
{
|
||||||
|
mLog.info("Removing idle recorder [" + entry.getKey() + "]");
|
||||||
|
it.remove();
|
||||||
|
entry.getValue().stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package record.wave;
|
package record.wave;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sample.ConversionUtils;
|
import sample.ConversionUtils;
|
||||||
@@ -58,7 +59,7 @@ public class MonoWaveReader implements AutoCloseable
|
|||||||
*/
|
*/
|
||||||
public MonoWaveReader(Path path, boolean realTime) throws IOException
|
public MonoWaveReader(Path path, boolean realTime) throws IOException
|
||||||
{
|
{
|
||||||
assert( path != null );
|
Validate.isTrue(path != null);
|
||||||
mPath = path;
|
mPath = path;
|
||||||
mRealTime = realTime;
|
mRealTime = realTime;
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,33 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* SDR Trunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014,2015 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 record.wave;
|
package record.wave;
|
||||||
|
|
||||||
|
import module.Module;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sample.ConversionUtils;
|
||||||
|
import sample.Listener;
|
||||||
|
import sample.real.IFilteredRealBufferListener;
|
||||||
|
import sample.real.RealBuffer;
|
||||||
|
import util.TimeStamp;
|
||||||
|
|
||||||
|
import javax.sound.sampled.AudioFormat;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@@ -26,206 +37,175 @@ 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.sound.sampled.AudioFormat;
|
|
||||||
|
|
||||||
import module.Module;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import sample.ConversionUtils;
|
|
||||||
import sample.Listener;
|
|
||||||
import sample.real.IFilteredRealBufferListener;
|
|
||||||
import sample.real.RealBuffer;
|
|
||||||
import util.TimeStamp;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WAVE audio recorder module for recording real sample buffers to a wave file
|
* WAVE audio recorder module for recording real sample buffers to a wave file
|
||||||
*/
|
*/
|
||||||
public class RealBufferWaveRecorder extends Module
|
public class RealBufferWaveRecorder extends Module
|
||||||
implements IFilteredRealBufferListener, Listener<RealBuffer>
|
implements IFilteredRealBufferListener, Listener<RealBuffer>
|
||||||
{
|
{
|
||||||
private final static Logger mLog =
|
private final static Logger mLog = LoggerFactory.getLogger(RealBufferWaveRecorder.class);
|
||||||
LoggerFactory.getLogger( RealBufferWaveRecorder.class );
|
|
||||||
|
|
||||||
private WaveWriter mWriter;
|
private WaveWriter mWriter;
|
||||||
private String mFilePrefix;
|
private String mFilePrefix;
|
||||||
private Path mFile;
|
private Path mFile;
|
||||||
private AudioFormat mAudioFormat;
|
private AudioFormat mAudioFormat;
|
||||||
|
|
||||||
private BufferProcessor mBufferProcessor;
|
|
||||||
private ScheduledFuture<?> mProcessorHandle;
|
|
||||||
private LinkedBlockingQueue<RealBuffer> mBuffers =
|
|
||||||
new LinkedBlockingQueue<>( 500 );
|
|
||||||
private long mLastBufferReceived;
|
|
||||||
|
|
||||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
|
||||||
|
|
||||||
public RealBufferWaveRecorder( int sampleRate, String filePrefix )
|
|
||||||
{
|
|
||||||
mAudioFormat = new AudioFormat( sampleRate, //SampleRate
|
|
||||||
16, //Sample Size
|
|
||||||
1, //Channels
|
|
||||||
true, //Signed
|
|
||||||
false ); //Little Endian
|
|
||||||
|
|
||||||
mFilePrefix = filePrefix;
|
private BufferProcessor mBufferProcessor;
|
||||||
}
|
private ScheduledFuture<?> mProcessorHandle;
|
||||||
|
private LinkedBlockingQueue<RealBuffer> mBuffers = new LinkedBlockingQueue<>(500);
|
||||||
|
private long mLastBufferReceived;
|
||||||
|
|
||||||
/**
|
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||||
* Timestamp of when the latest buffer was received by this recorder
|
|
||||||
*/
|
|
||||||
public long getLastBufferReceived()
|
|
||||||
{
|
|
||||||
return mLastBufferReceived;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getFile()
|
public RealBufferWaveRecorder(int sampleRate, String filePrefix)
|
||||||
{
|
{
|
||||||
return mFile;
|
mAudioFormat = new AudioFormat(sampleRate, //SampleRate
|
||||||
}
|
16, //Sample Size
|
||||||
|
1, //Channels
|
||||||
public void start( ScheduledExecutorService executor )
|
true, //Signed
|
||||||
{
|
false); //Little Endian
|
||||||
if( mRunning.compareAndSet( false, true ) )
|
|
||||||
{
|
|
||||||
if( mBufferProcessor == null )
|
|
||||||
{
|
|
||||||
mBufferProcessor = new BufferProcessor();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
mFilePrefix = filePrefix;
|
||||||
{
|
}
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append( mFilePrefix );
|
|
||||||
sb.append( "_" );
|
|
||||||
sb.append( TimeStamp.getLongTimeStamp( "_" ) );
|
|
||||||
sb.append( ".wav" );
|
|
||||||
|
|
||||||
mFile = Paths.get( sb.toString() );
|
|
||||||
|
|
||||||
mWriter = new WaveWriter( mAudioFormat, mFile );
|
/**
|
||||||
|
* Indicates if the recorder is currently running.
|
||||||
|
*/
|
||||||
|
public boolean isRunning()
|
||||||
|
{
|
||||||
|
return mRunning.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp of when the latest buffer was received by this recorder
|
||||||
|
*/
|
||||||
|
public long getLastBufferReceived()
|
||||||
|
{
|
||||||
|
return mLastBufferReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getFile()
|
||||||
|
{
|
||||||
|
return mFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(ScheduledExecutorService executor)
|
||||||
|
{
|
||||||
|
if(mRunning.compareAndSet(false, true))
|
||||||
|
{
|
||||||
|
if(mBufferProcessor == null)
|
||||||
|
{
|
||||||
|
mBufferProcessor = new BufferProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(mFilePrefix);
|
||||||
|
sb.append("_");
|
||||||
|
sb.append(TimeStamp.getLongTimeStamp("_"));
|
||||||
|
sb.append(".wav");
|
||||||
|
|
||||||
|
mFile = Paths.get(sb.toString());
|
||||||
|
|
||||||
|
mWriter = new WaveWriter(mAudioFormat, mFile);
|
||||||
|
|
||||||
/* Schedule the processor to run every 500 milliseconds */
|
/* Schedule the processor to run every 500 milliseconds */
|
||||||
mProcessorHandle = executor.scheduleAtFixedRate(
|
mProcessorHandle = executor.scheduleAtFixedRate(mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS);
|
||||||
mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS );
|
|
||||||
}
|
|
||||||
catch( IOException io )
|
|
||||||
{
|
|
||||||
mLog.error( "Error starting real buffer recorder", io );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stop()
|
|
||||||
{
|
|
||||||
/* Signal the buffer processor to end with a poison pill */
|
|
||||||
receive( new PoisonPill() );
|
|
||||||
|
|
||||||
mRunning.set( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receive( RealBuffer buffer )
|
|
||||||
{
|
|
||||||
if( mRunning.get() )
|
|
||||||
{
|
|
||||||
boolean success = mBuffers.offer( buffer );
|
|
||||||
|
|
||||||
if( !success )
|
|
||||||
{
|
|
||||||
mLog.error( "recorder buffer overflow - purging [" +
|
|
||||||
mFile.toFile().getAbsolutePath() + "]" );
|
|
||||||
|
|
||||||
mBuffers.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
mLastBufferReceived = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Listener<RealBuffer> getFilteredRealBufferListener()
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispose()
|
|
||||||
{
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BufferProcessor implements Runnable
|
|
||||||
{
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
RealBuffer buffer = mBuffers.poll();
|
|
||||||
|
|
||||||
while( buffer != null )
|
|
||||||
{
|
|
||||||
if( buffer instanceof PoisonPill )
|
|
||||||
{
|
|
||||||
buffer = null;
|
|
||||||
|
|
||||||
mBuffers.clear();
|
|
||||||
|
|
||||||
if( mWriter != null )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mWriter.close();
|
|
||||||
}
|
|
||||||
catch( IOException io )
|
|
||||||
{
|
|
||||||
mLog.error( "Error stopping complex wave recorder [" +
|
|
||||||
getFile() + "]", io );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( mProcessorHandle != null )
|
|
||||||
{
|
|
||||||
mProcessorHandle.cancel( true );
|
|
||||||
}
|
|
||||||
|
|
||||||
mProcessorHandle = null;
|
|
||||||
mWriter = null;
|
|
||||||
mFile = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mWriter.write( ConversionUtils.convertToSigned16BitSamples( buffer ) );
|
|
||||||
buffer = mBuffers.poll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch ( IOException ioe )
|
catch(IOException io)
|
||||||
{
|
{
|
||||||
/* Stop this module if/when we get an IO exception */
|
mLog.error("Error starting real buffer recorder", io);
|
||||||
mBuffers.clear();
|
}
|
||||||
stop();
|
}
|
||||||
|
}
|
||||||
mLog.error( "IOException while trying to write to the wave "
|
|
||||||
+ "writer", ioe );
|
public void stop()
|
||||||
}
|
{
|
||||||
}
|
if(mRunning.compareAndSet(true, false))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
write();
|
||||||
|
|
||||||
|
if(mWriter != null)
|
||||||
|
{
|
||||||
|
mWriter.close();
|
||||||
|
mWriter = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(IOException ioe)
|
||||||
|
{
|
||||||
|
mLog.error("Error writing final audio buffers to recording during shutdown", ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receive(RealBuffer buffer)
|
||||||
|
{
|
||||||
|
if(mRunning.get())
|
||||||
|
{
|
||||||
|
boolean success = mBuffers.offer(buffer);
|
||||||
|
|
||||||
|
if(!success)
|
||||||
|
{
|
||||||
|
mLog.error("recorder buffer overflow - purging [" + mFile.toFile().getAbsolutePath() + "]");
|
||||||
|
mBuffers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastBufferReceived = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Listener<RealBuffer> getFilteredRealBufferListener()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes all audio currently in the queue to the file
|
||||||
|
* @throws IOException if there are any errors writing the audio
|
||||||
|
*/
|
||||||
|
private void write() throws IOException
|
||||||
|
{
|
||||||
|
RealBuffer buffer = mBuffers.poll();
|
||||||
|
|
||||||
|
while(buffer != null)
|
||||||
|
{
|
||||||
|
mWriter.write(ConversionUtils.convertToSigned16BitSamples(buffer));
|
||||||
|
buffer = mBuffers.poll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BufferProcessor implements Runnable
|
||||||
|
{
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
write();
|
||||||
|
}
|
||||||
|
catch(IOException ioe)
|
||||||
|
{
|
||||||
|
/* Stop this module if/when we get an IO exception */
|
||||||
|
mBuffers.clear();
|
||||||
|
stop();
|
||||||
|
|
||||||
|
mLog.error("IO Exception while trying to write to the wave writer", ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This is used as a sentinel value to signal the buffer processor to end
|
|
||||||
*/
|
|
||||||
public class PoisonPill extends RealBuffer
|
|
||||||
{
|
|
||||||
public PoisonPill()
|
|
||||||
{
|
|
||||||
super( new float[ 1 ] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* 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 record.wave;
|
package record.wave;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
@@ -32,10 +35,7 @@ import javax.sound.sampled.AudioFormat;
|
|||||||
|
|
||||||
public class WaveWriter implements AutoCloseable
|
public class WaveWriter implements AutoCloseable
|
||||||
{
|
{
|
||||||
// private final static Logger mLog = LoggerFactory.getLogger( WaveWriter.class );
|
private static final Pattern FILENAME_PATTERN = Pattern.compile( "(.*_)(\\d+)(\\.wav)" );
|
||||||
|
|
||||||
private static final Pattern FILENAME_PATTERN =
|
|
||||||
Pattern.compile( "(.*_)(\\d+)(\\.wav)" );
|
|
||||||
public static final long MAX_WAVE_SIZE = 2l * (long)Integer.MAX_VALUE;
|
public static final long MAX_WAVE_SIZE = 2l * (long)Integer.MAX_VALUE;
|
||||||
|
|
||||||
private AudioFormat mAudioFormat;
|
private AudioFormat mAudioFormat;
|
||||||
@@ -56,11 +56,10 @@ public class WaveWriter implements AutoCloseable
|
|||||||
* @param maxSize - maximum file size ( range: 1 - 4,294,967,294 bytes )
|
* @param maxSize - maximum file size ( range: 1 - 4,294,967,294 bytes )
|
||||||
* @throws IOException - if there are any IO issues
|
* @throws IOException - if there are any IO issues
|
||||||
*/
|
*/
|
||||||
public WaveWriter( AudioFormat format, Path file, long maxSize )
|
public WaveWriter( AudioFormat format, Path file, long maxSize ) throws IOException
|
||||||
throws IOException
|
|
||||||
{
|
{
|
||||||
assert( format != null );
|
Validate.isTrue(format != null);
|
||||||
assert( file != null );
|
Validate.isTrue( file != null );
|
||||||
|
|
||||||
mAudioFormat = format;
|
mAudioFormat = format;
|
||||||
mFile = file;
|
mFile = file;
|
||||||
@@ -100,15 +99,11 @@ public class WaveWriter implements AutoCloseable
|
|||||||
|
|
||||||
while( Files.exists( mFile ) )
|
while( Files.exists( mFile ) )
|
||||||
{
|
{
|
||||||
mFile = Paths.get( mFile.toFile().getAbsolutePath()
|
mFile = Paths.get( mFile.toFile().getAbsolutePath().replace( ".wav", "_" + version + ".wav" ) );
|
||||||
.replace( ".wav", "_" + version + ".wav" ) );
|
|
||||||
|
|
||||||
version++;
|
version++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Files.createFile( mFile );
|
mFileChannel = (FileChannel.open( mFile, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW ) );
|
||||||
|
|
||||||
mFileChannel = (FileChannel.open( mFile, StandardOpenOption.WRITE ) );
|
|
||||||
|
|
||||||
ByteBuffer header = WaveUtils.getWaveHeader( mAudioFormat );
|
ByteBuffer header = WaveUtils.getWaveHeader( mAudioFormat );
|
||||||
|
|
||||||
@@ -127,9 +122,18 @@ public class WaveWriter implements AutoCloseable
|
|||||||
{
|
{
|
||||||
mFileChannel.force( true );
|
mFileChannel.force( true );
|
||||||
mFileChannel.close();
|
mFileChannel.close();
|
||||||
|
mFileChannel = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
|
protected void finalize() throws IOException
|
||||||
|
{
|
||||||
|
mFileChannel.force( true );
|
||||||
|
mFileChannel.close();
|
||||||
|
mFileChannel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* Writes the buffer contents to the file. Assumes that the buffer is full
|
* Writes the buffer contents to the file. Assumes that the buffer is full
|
||||||
* and the first byte of data is at position 0.
|
* and the first byte of data is at position 0.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package sample.adapter;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import source.mixer.MixerChannel;
|
import source.mixer.MixerChannel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,7 +35,7 @@ public class ChannelShortAdapter implements ISampleAdapter
|
|||||||
public ChannelShortAdapter( MixerChannel channel )
|
public ChannelShortAdapter( MixerChannel channel )
|
||||||
{
|
{
|
||||||
/* Only use with LEFT/RIGHT channels */
|
/* Only use with LEFT/RIGHT channels */
|
||||||
assert( channel != MixerChannel.MONO );
|
Validate.isTrue(channel != MixerChannel.MONO);
|
||||||
|
|
||||||
mMixerChannel = channel;
|
mMixerChannel = channel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package sample.complex;
|
package sample.complex;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import sample.Listener;
|
import sample.Listener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,7 +33,7 @@ public class ComplexToComplexBufferAssembler implements ComplexSampleListener
|
|||||||
|
|
||||||
public ComplexToComplexBufferAssembler( int bufferSize )
|
public ComplexToComplexBufferAssembler( int bufferSize )
|
||||||
{
|
{
|
||||||
assert( bufferSize % 2 == 0 );
|
Validate.isTrue(bufferSize % 2 == 0);
|
||||||
|
|
||||||
mBufferSize = bufferSize;
|
mBufferSize = bufferSize;
|
||||||
mBuffer = new float[ mBufferSize ];
|
mBuffer = new float[ mBufferSize ];
|
||||||
|
|||||||
@@ -113,7 +113,16 @@ public class FCD2TunerController extends FCDTunerController
|
|||||||
FCD2TunerConfiguration fcd2 = (FCD2TunerConfiguration)config;
|
FCD2TunerConfiguration fcd2 = (FCD2TunerConfiguration)config;
|
||||||
|
|
||||||
setFrequencyCorrection(fcd2.getFrequencyCorrection());
|
setFrequencyCorrection(fcd2.getFrequencyCorrection());
|
||||||
setFrequency(fcd2.getFrequency());
|
|
||||||
|
long frequency = MINIMUM_TUNABLE_FREQUENCY;
|
||||||
|
|
||||||
|
if(fcd2.getFrequency() >= MINIMUM_TUNABLE_FREQUENCY &&
|
||||||
|
fcd2.getFrequency() <= MAXIMUM_TUNABLE_FREQUENCY)
|
||||||
|
{
|
||||||
|
frequency = fcd2.getFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
setFrequency(frequency);
|
||||||
setLNAGain(fcd2.getGainLNA());
|
setLNAGain(fcd2.getGainLNA());
|
||||||
setMixerGain(fcd2.getGainMixer());
|
setMixerGain(fcd2.getGainMixer());
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,7 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -282,7 +283,7 @@ public class SpectrumPanel extends JPanel
|
|||||||
*/
|
*/
|
||||||
public void setZoom( int zoom )
|
public void setZoom( int zoom )
|
||||||
{
|
{
|
||||||
assert( 0 <= zoom && zoom <= 5 );
|
Validate.isTrue( 0 <= zoom && zoom <= 5 );
|
||||||
|
|
||||||
mZoom = zoom;
|
mZoom = zoom;
|
||||||
}
|
}
|
||||||
@@ -305,7 +306,7 @@ public class SpectrumPanel extends JPanel
|
|||||||
*/
|
*/
|
||||||
public void setSampleSize( double sampleSize )
|
public void setSampleSize( double sampleSize )
|
||||||
{
|
{
|
||||||
assert( 2.0 <= sampleSize && sampleSize <= 32.0 );
|
Validate.isTrue(2.0 <= sampleSize && sampleSize <= 32.0);
|
||||||
|
|
||||||
mDBScale = (float)( 20.0 * Math.log10( Math.pow( 2.0, sampleSize - 1 ) ) );
|
mDBScale = (float)( 20.0 * Math.log10( Math.pow( 2.0, sampleSize - 1 ) ) );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package util;
|
package util;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class XTEA
|
public class XTEA
|
||||||
@@ -58,7 +60,7 @@ public class XTEA
|
|||||||
*/
|
*/
|
||||||
public void setKey(byte[] key)
|
public void setKey(byte[] key)
|
||||||
{
|
{
|
||||||
assert(key.length == 16);
|
Validate.isTrue(key.length == 16);
|
||||||
|
|
||||||
int keyInts[] = new int[8];
|
int keyInts[] = new int[8];
|
||||||
|
|
||||||
@@ -94,7 +96,7 @@ public class XTEA
|
|||||||
*/
|
*/
|
||||||
public byte[] encrypt(byte[] plainText)
|
public byte[] encrypt(byte[] plainText)
|
||||||
{
|
{
|
||||||
assert(plainText.length == 8);
|
Validate.isTrue(plainText.length == 8);
|
||||||
|
|
||||||
//Convert the 8 bytes of plaintext to 2 integers
|
//Convert the 8 bytes of plaintext to 2 integers
|
||||||
int result0 = (plainText[3] << 0) & MASK0 | (plainText[2] << 8) & MASK1 |
|
int result0 = (plainText[3] << 0) & MASK0 | (plainText[2] << 8) & MASK1 |
|
||||||
|
|||||||
Reference in New Issue
Block a user