Interim checkin for call recording.

This commit is contained in:
denny
2015-09-11 14:01:47 -04:00
parent 0f85b7260a
commit 575197bd98
93 changed files with 2349 additions and 2637 deletions

View File

@@ -27,10 +27,10 @@ import settings.SettingChangeListener;
import settings.SettingsManager;
import settings.ColorSetting.ColorSettingName;
import alias.Alias;
import alias.Metadata;
import alias.MetadataType;
import audio.AudioEvent.Type;
import audio.metadata.AudioMetadata;
import audio.metadata.Metadata;
import audio.metadata.MetadataType;
public class AudioChannelPanel extends JPanel
implements Listener<AudioEvent>, SettingChangeListener
@@ -51,14 +51,14 @@ public class AudioChannelPanel extends JPanel
private JLabel mChannelName;
private JLabel mMutedLabel;
private JLabel mFromLabel = new JLabel( "From:" );
private JLabel mFrom = new JLabel( "" );
private JLabel mFromAlias = new JLabel( "" );
private JLabel mToLabel = new JLabel( "To:" );
private JLabel mTo = new JLabel( "" );
private JLabel mToAlias = new JLabel( "" );
private JLabel mFromLabel = new JLabel( "From:" );
private JLabel mFrom = new JLabel( "" );
private JLabel mFromAlias = new JLabel( "" );
private boolean mConfigured = false;
private BooleanControl mMuteControl;
@@ -138,6 +138,18 @@ public class AudioChannelPanel extends JPanel
mMutedLabel.setForeground( Color.RED );
add( mMutedLabel, "wrap" );
mToLabel.setFont( mFont );
mToLabel.setForeground( mLabelColor );
add( mToLabel );
mTo.setFont( mFont );
mTo.setForeground( mLabelColor );
add( mTo );
mToAlias.setFont( mFont );
mToAlias.setForeground( mLabelColor );
add( mToAlias,"wrap" );
mFromLabel.setFont( mFont );
mFromLabel.setForeground( mLabelColor );
@@ -150,18 +162,6 @@ public class AudioChannelPanel extends JPanel
mFromAlias.setFont( mFont );
mFromAlias.setForeground( mLabelColor );
add( mFromAlias,"wrap" );
mToLabel.setFont( mFont );
mToLabel.setForeground( mLabelColor );
add( mToLabel );
mTo.setFont( mFont );
mTo.setForeground( mLabelColor );
add( mTo );
mToAlias.setFont( mFont );
mToAlias.setForeground( mLabelColor );
add( mToAlias );
}
private void updateMuteState()
@@ -287,7 +287,7 @@ public class AudioChannelPanel extends JPanel
{
if( !mConfigured || audioMetadata.isUpdated() )
{
final Metadata from = audioMetadata.getMetadata( MetadataType.FROM_TALKGROUP );
final Metadata from = audioMetadata.getMetadata( MetadataType.FROM );
final Metadata to = audioMetadata.getMetadata( MetadataType.TO );
@@ -296,11 +296,17 @@ public class AudioChannelPanel extends JPanel
@Override
public void run()
{
mFrom.setText( from.getValue() );
updateAlias( mFromAlias, from.getAlias() );
if( from != null )
{
mFrom.setText( from.getValue() );
updateAlias( mFromAlias, from.getAlias() );
}
mTo.setText( to.getValue() );
updateAlias( mToAlias, to.getAlias() );
if( to != null )
{
mTo.setText( to.getValue() );
updateAlias( mToAlias, to.getAlias() );
}
}
} );

View File

@@ -38,7 +38,6 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
private Map<String,AudioOutput> mChannelMap = new HashMap<>();
private List<String> mChannelNames = new ArrayList<>();
private AudioOutput mAudioOutput;
private boolean mMuted = false;
private Listener<AudioEvent> mConfigurationChangeListener;
@@ -90,10 +89,6 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
@Override
public void run()
{
if( !mMuted )
{
}
removeCompletedChannelAssignments();
if( mAudioPacketQueue != null )
@@ -104,55 +99,62 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
for( AudioPacket packet: packets )
{
int source = packet.getMetadata().getSource();
/* Existing channel assignment */
if( mChannelAssignments.containsKey( source ) )
int source = packet.getAudioMetadata().getSource();
if( packet.getType() == AudioPacket.Type.END )
{
mChannelAssignments.get( source ).getAudioOutput()
.receive( packet );
}
/* Start a new channel assignment */
else if( !mAvailableAudioOutputs.isEmpty() )
{
AudioOutput channel = mAvailableAudioOutputs.remove( 0 );
mChannelAssignments.put( packet.getMetadata().getSource(),
new AudioChannelAssignment( channel, packet.getMetadata().getPriority() ) );
updateLowestPriorityAssignment();
channel.receive( packet );
}
/* Override the lowest priority assignment */
else
{
if( mLowestPrioritySource > -1 )
/* Existing channel assignment */
if( mChannelAssignments.containsKey( source ) )
{
int priority = packet.getMetadata().getPriority();
if( packet.getMetadata().isSelected() ||
priority < mChannelAssignments
.get( mLowestPrioritySource ).getPriority() )
mChannelAssignments.get( source ).getAudioOutput()
.receive( packet );
}
/* Start a new channel assignment */
else if( !mAvailableAudioOutputs.isEmpty() )
{
AudioOutput channel = mAvailableAudioOutputs.remove( 0 );
mChannelAssignments.put( packet.getAudioMetadata().getSource(),
new AudioChannelAssignment( channel, packet.getAudioMetadata().getPriority() ) );
updateLowestPriorityAssignment();
channel.receive( packet );
}
/* Override the lowest priority assignment */
else
{
if( mLowestPrioritySource > -1 )
{
AudioChannelAssignment assignment =
mChannelAssignments.remove( mLowestPrioritySource );
if( assignment != null )
{
assignment.setPriority( priority );
int priority = packet.getAudioMetadata().getPriority();
int overrideSource = packet.getMetadata().getSource();
mChannelAssignments.put( overrideSource, assignment );
mLowestPrioritySource = overrideSource;
assignment.getAudioOutput().receive( packet );
}
else
if( packet.getAudioMetadata().isSelected() ||
priority < mChannelAssignments
.get( mLowestPrioritySource ).getPriority() )
{
mLowestPrioritySource = -1;
AudioChannelAssignment assignment =
mChannelAssignments.remove( mLowestPrioritySource );
if( assignment != null )
{
assignment.setPriority( priority );
int overrideSource = packet.getAudioMetadata().getSource();
mChannelAssignments.put( overrideSource, assignment );
mLowestPrioritySource = overrideSource;
assignment.getAudioOutput().receive( packet );
}
else
{
mLowestPrioritySource = -1;
}
}
}
}

View File

@@ -8,9 +8,9 @@ import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.real.IRealBufferListener;
import sample.real.RealBuffer;
import alias.Metadata;
import audio.metadata.AudioMetadata;
import audio.metadata.IMetadataListener;
import audio.metadata.Metadata;
import audio.squelch.ISquelchStateListener;
import audio.squelch.SquelchState;
import controller.channel.ChannelEvent;
@@ -61,16 +61,11 @@ public class AudioModule extends Module implements IAudioPacketProvider,
private boolean mRemoveDC = false;
public AudioModule()
{
this( false );
}
public AudioModule( boolean removeDC )
public AudioModule( boolean record, boolean removeDC )
{
mSourceID = ++UNIQUE_ID;
mAudioMetadata = new AudioMetadata( mSourceID );
mAudioMetadata = new AudioMetadata( mSourceID, record );
mRemoveDC = removeDC;
@@ -89,10 +84,28 @@ public class AudioModule extends Module implements IAudioPacketProvider,
}
@Override
public void init()
public void reset()
{
mAudioMetadata.reset();
}
@Override
public void start()
{
/* No start operations provided */
}
@Override
public void stop()
{
// /* Issue an end audio packet in case a recorder is still rolling */
// if( mAudioPacketListener != null )
// {
// mLog.debug( "Sending end audio packet - stop() invoked" );
// mAudioPacketListener.receive( new AudioPacket( AudioPacket.Type.END,
// mAudioMetadata.copyOf() ) );
// }
}
/**
* Processes demodulated audio samples into audio packets with current audio
@@ -108,6 +121,9 @@ public class AudioModule extends Module implements IAudioPacketProvider,
mAudioFilter.filter( audio );
AudioPacket packet = new AudioPacket( audio, mAudioMetadata.copyOf() );
/* Reset the updated flag on the metadata */
mAudioMetadata.setUpdated( false );
mAudioPacketListener.receive( packet );
}
@@ -185,6 +201,12 @@ public class AudioModule extends Module implements IAudioPacketProvider,
@Override
public void receive( SquelchState state )
{
if( state == SquelchState.SQUELCH && mAudioPacketListener != null )
{
mAudioPacketListener.receive( new AudioPacket( AudioPacket.Type.END,
mAudioMetadata.copyOf() ) );
}
mSquelchState = state;
}
}

View File

@@ -1,27 +1,50 @@
package audio;
import sample.real.RealBuffer;
import audio.metadata.AudioMetadata;
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 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/>
******************************************************************************/
public class AudioPacket
{
private Type mType;
private float[] mAudioData;
private RealBuffer mAudioData;
private AudioMetadata mAudioMetadata;
public AudioPacket( Type type )
public AudioPacket( Type type, AudioMetadata metadata )
{
mType = type;
mAudioMetadata = metadata;
}
public AudioPacket( float[] audio, AudioMetadata metadata )
{
this( Type.AUDIO );
this( Type.AUDIO, metadata );
mAudioData = audio;
mAudioMetadata = metadata;
mAudioData = new RealBuffer( audio );
}
public AudioMetadata getMetadata()
public boolean hasAudioMetadata()
{
return mAudioMetadata != null;
}
public AudioMetadata getAudioMetadata()
{
return mAudioMetadata;
}
@@ -36,7 +59,7 @@ public class AudioPacket
return mType;
}
public float[] getAudioData()
public RealBuffer getAudioBuffer()
{
return mAudioData;
}

View File

@@ -34,7 +34,7 @@ public class AudioPacketRateCounter implements Listener<AudioPacket>
mStartTime = System.currentTimeMillis();
}
mCount += packet.getAudioData().length;
mCount += packet.getAudioBuffer().getSamples().length;
long elapsed = System.currentTimeMillis() - mStartTime;

View File

@@ -0,0 +1,8 @@
package audio;
import sample.Listener;
public interface IAudioPacketListener
{
public Listener<AudioPacket> getAudioPacketListener();
}

View File

@@ -8,38 +8,92 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import alias.Metadata;
import alias.MetadataType;
import alias.Alias;
import alias.priority.Priority;
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 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/>
******************************************************************************/
public class AudioMetadata implements Listener<Metadata>
{
protected static final Logger mLog = LoggerFactory.getLogger( AudioMetadata.class );
private int mSource;
private int mPriority = 99;
private boolean mSelected = false;
private List<Metadata> mMetadata = new ArrayList<>();
private boolean mUpdated = false;
/* Channel Selectect == highest priority */
private boolean mSelected = false;
/* Call Priority */
private int mPriority = Priority.DEFAULT_PRIORITY;
/* Indicates if the overall source is recordable */
private boolean mSourceRecordable = false;
public AudioMetadata( int source )
/* Indicates the current recordable state */
private boolean mRecordable = false;
/* A unique identifier for this metadata */
private String mIdentifier;
public AudioMetadata( int source, boolean recordable )
{
mSource = source;
setIdentifier( mSource );
mSourceRecordable = recordable;
setRecordable( mSourceRecordable );
}
/**
* Returns a unique identifier for this audio metadata that contains the
* source id and optionally the TO metadata string.
*/
public String getIdentifier()
{
return mIdentifier;
}
public void setIdentifier( int source )
{
setIdentifier( source, null );
}
public void setIdentifier( int source, String id )
{
mIdentifier = "SRC:" + source + " ID:" + ( id == null ? "UNKNOWN" : id );
mUpdated = true;
}
/**
* Returns a copy of the current audio metadata.
*/
public AudioMetadata copyOf()
{
AudioMetadata copy = new AudioMetadata( mSource );
AudioMetadata copy = new AudioMetadata( mSource, mSourceRecordable );
copy.mPriority = mPriority;
copy.mSelected = mSelected;
copy.mRecordable = mRecordable;
copy.mMetadata.addAll( mMetadata );
if( mUpdated )
{
copy.mUpdated = true;
mUpdated = false;
}
copy.mUpdated = mUpdated;
return copy;
}
@@ -58,10 +112,42 @@ public class AudioMetadata implements Listener<Metadata>
it.remove();
}
}
/* Update the recordable state, identifier and audio call priority levels */
mRecordable = mSourceRecordable;
setIdentifier( mSource );
mPriority = Priority.DEFAULT_PRIORITY;
for( Metadata metadata: mMetadata )
{
if( metadata.hasAlias() )
{
Alias alias = metadata.getAlias();
if( metadata.getMetadataType() == MetadataType.FROM ||
metadata.getMetadataType() == MetadataType.TO )
{
if( !alias.isRecordable() )
{
mRecordable = false;
}
}
if( metadata.getMetadataType() == MetadataType.TO )
{
setIdentifier( mSource, metadata.getValue() );
}
if( alias != null && alias.getCallPriority() < mPriority )
{
mPriority = alias.getCallPriority();
}
}
}
mUpdated = true;
}
/**
* Indicates if this audio metadata contains updated information. The flag
* will only be set when new data is added and will be reset by a copyOf()
@@ -71,6 +157,11 @@ public class AudioMetadata implements Listener<Metadata>
{
return mUpdated;
}
public void setUpdated( boolean updated )
{
mUpdated = updated;
}
/**
* Adds the metadata. Any temporal metadata will be removed when the
@@ -89,7 +180,23 @@ public class AudioMetadata implements Listener<Metadata>
if( metadata.hasAlias() )
{
//TODO: assign priority from alias here
Alias alias = metadata.getAlias();
/* Capture non-recordable aliases */
if( metadata.getMetadataType() == MetadataType.FROM ||
metadata.getMetadataType() == MetadataType.TO )
{
if( !alias.isRecordable() )
{
mRecordable = false;
}
}
/* Capture call priority levels from aliases */
if( alias.getCallPriority() < mPriority )
{
mPriority = alias.getCallPriority();
}
}
}
@@ -137,13 +244,10 @@ public class AudioMetadata implements Listener<Metadata>
}
/**
* Sets the priority of this audio packet in the range of 1 - Max Integer value
*
* @param priority (1 - Max Int)
*/
* Sets the priority of this audio packet within the defined min/max priority range */
public void setPriority( int priority )
{
assert( 1 <= priority && priority <= Integer.MAX_VALUE );
assert( Priority.MIN_PRIORITY <= priority && priority <= Priority.MAX_PRIORITY );
mPriority = priority;
@@ -177,4 +281,18 @@ public class AudioMetadata implements Listener<Metadata>
{
return mSelected;
}
/**
* Indicates if audio associated with this metadata is recordable.
*/
public boolean isRecordable()
{
return mRecordable;
}
public void setRecordable( boolean recordable )
{
mRecordable = recordable;
mUpdated = true;
}
}

View File

@@ -1,7 +1,6 @@
package audio.metadata;
import sample.Listener;
import alias.Metadata;
public interface IMetadataListener
{

View File

@@ -1,7 +1,6 @@
package audio.metadata;
import sample.Listener;
import alias.Metadata;
public interface IMetadataProvider
{

View File

@@ -0,0 +1,104 @@
package audio.metadata;
import alias.Alias;
public class Metadata
{
private MetadataType mMetadataType;
private String mValue;
private Alias mValueAlias;
private boolean mTemporal;
/**
* Constucts a metadata object with the temporal settings. Temporal
* indicates that this metadata will be removed at the end of a reset
* event, or call end event.
*/
public Metadata( MetadataType metadataType, String value, Alias alias, boolean temporal )
{
mMetadataType = metadataType;
mValue = value;
mValueAlias = alias;
mTemporal = temporal;
}
/**
* Constucts a metadata object with the temporal settings. Temporal
* indicates that this metadata will be removed at the end of a reset
* event, or call end event.
*/
public Metadata( MetadataType metadataType, String value, boolean temporal )
{
this( metadataType, value, null, temporal );
}
/**
* Constucts a non-temporal metadata object
*/
public Metadata( MetadataType metadataType, String value, Alias alias )
{
this( metadataType, value, alias, false );
}
/**
* Constucts a non-temporal metadata object
*/
public Metadata( MetadataType metadataType, String value )
{
this( metadataType, value, null, false );
}
public MetadataType getMetadataType()
{
return mMetadataType;
}
public String getKey()
{
return mMetadataType.getLabel();
}
public String getValue()
{
return mValue;
}
public Alias getAlias()
{
return mValueAlias;
}
public boolean hasAlias()
{
return mValueAlias != null;
}
/**
* Indicates that this piece of metadata is only valid for the duration of
* a call event and should be removed during a reset
*/
public boolean isTemporal()
{
return mTemporal;
}
public boolean isReset()
{
return false;
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( mMetadataType.getLabel() );
sb.append( ":" );
sb.append( mValue );
sb.append( " [" );
sb.append( mMetadataType.name() );
sb.append( "] temporal:" );
sb.append( mTemporal );
return sb.toString();
}
}

View File

@@ -0,0 +1,14 @@
package audio.metadata;
public class MetadataReset extends Metadata
{
public MetadataReset()
{
super( MetadataType.TEMPORAL_RESET, "reset", true );
}
public boolean isReset()
{
return true;
}
}

View File

@@ -0,0 +1,40 @@
package audio.metadata;
public enum MetadataType
{
CHANNEL_NUMBER( "Channel Number" ),
CHANNEL_NAME( "Channel Name" ),
DETAILS( "Details" ),
ESN( "ESN" ),
FLEETSYNC( "Fleetsync" ),
FREQUENCY( "Frequency" ),
FROM( "From" ),
MDC1200( "MDC-1200" ),
MOBILE_ID( "Mobile ID" ),
MPT1327( "MPT-1327" ),
PROTOCOL( "Protocol" ),
SITE_ID( "Site ID" ),
STATUS( "Status" ),
SYSTEM( "System" ),
TO( "To" ),
UNIQUE_ID( "Unique ID" ),
TEMPORAL_RESET( "Reset" );
private String mLabel;
private MetadataType( String label )
{
mLabel = label;
}
public String getLabel()
{
return mLabel;
}
public String toString()
{
return mLabel;
}
}

View File

@@ -176,7 +176,7 @@ public class MonoAudioOutput extends AudioOutput
}
else if( packet.getType() == Type.AUDIO )
{
float[] samples = packet.getAudioData();
float[] samples = packet.getAudioBuffer().getSamples();
/* Little-endian byte buffer */
ByteBuffer buffer =
@@ -195,7 +195,7 @@ public class MonoAudioOutput extends AudioOutput
checkStart();
broadcast( packet.getMetadata() );
broadcast( packet.getAudioMetadata() );
}
}
}