Initial commit of redesigned internal processing and audio management.

Note: Several of the decoders are currently non-functioning in this
commit.  If you want the latest functioning codebase, please checkout
the commit prior to this one.  All decoders will eventually be updated
with this new architecture.

Currently broken:
- Logging and recording
- Aux decoders (Fleetsync/MDC/LoJack/Tait)
- LTR/Passport decoders

Changes in this commit:
- Module architecture for decoders and components with central
provisioning by the DecoderFactory
- Transition from Complex sample stream processing to float buffer
processing for improved memory management
- Redesigned filter structures - to support buffers and streams
- Resolved several memory leak issues
- Universal sharing of the thread pool manager
- Added centralized audio management with call/user prioritization and
new gui with mute/volume controls
- Embedded audio metadata for eventual streaming support
- Redesigned ProcessingChain to support independent re-use
- Added new TrafficChannelManager to handle traffic channel allocations
- Traffic Channel pool size and Call timeout available in P25 and
MPT1327 configs
- Redesigned channel and decoder gui components
This commit is contained in:
denny
2015-09-01 07:10:26 -04:00
parent 538a299246
commit f4d8ea281d
570 changed files with 21578 additions and 17012 deletions

View File

@@ -1,12 +1,10 @@
package source;
import java.util.List;
import sample.Provider;
import sample.complex.ComplexSample;
import sample.SampleType;
import sample.complex.ComplexBuffer;
public abstract class ComplexSource extends Source
implements Provider<List<ComplexSample>>
public abstract class ComplexSource extends Source implements Provider<ComplexBuffer>
{
public ComplexSource( String name )
{

View File

@@ -1,6 +1,7 @@
package source;
import sample.Provider;
import sample.SampleType;
public abstract class FloatSource extends Source implements Provider<Float>
{

View File

@@ -1,6 +1,7 @@
package source;
import sample.Provider;
import sample.SampleType;
import sample.real.RealBuffer;

View File

@@ -17,7 +17,7 @@
******************************************************************************/
package source;
import source.Source.SampleType;
import sample.SampleType;
public interface SampleProvider
{

View File

@@ -17,6 +17,8 @@
******************************************************************************/
package source;
import sample.SampleType;
/**
* Abstract class to define the minimum functionality of a sample data provider.
*/
@@ -25,8 +27,6 @@ public abstract class Source
protected String mName;
protected SampleType mSampleType;
public enum SampleType { COMPLEX, REAL }
public Source( String name, SampleType sampleType )
{
mName = name;

View File

@@ -23,13 +23,13 @@ import java.awt.event.ActionListener;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import net.miginfocom.swing.MigLayout;
import source.config.SourceConfigFactory;
import source.config.SourceConfiguration;
import controller.Editor;
import controller.channel.AbstractChannelEditor;
import controller.channel.ChannelNode;
import controller.channel.ChannelValidationException;
public class SourceComponentEditor extends AbstractChannelEditor
{
@@ -42,7 +42,9 @@ public class SourceComponentEditor extends AbstractChannelEditor
{
super( channelNode );
mComboSources = new JComboBox<SourceType>();
setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) );
mComboSources = new JComboBox<SourceType>();
mComboSources.setModel( new DefaultComboBoxModel<SourceType>( SourceType.values() ) );
mComboSources.addActionListener( new ActionListener()
{
@@ -86,6 +88,7 @@ public class SourceComponentEditor extends AbstractChannelEditor
}
});
add( new JLabel( "Source:" ) );
add( mComboSources, "wrap" );
reset();

View File

@@ -17,9 +17,9 @@
******************************************************************************/
package source;
import source.config.SourceConfiguration;
import source.mixer.MixerManager;
import controller.ResourceManager;
import controller.channel.ProcessingChain;
public class SourceManager {
/**
@@ -40,28 +40,25 @@ public class SourceManager {
mResourceManager = resourceManager;
}
public Source getSource( ProcessingChain channel ) throws SourceException
public Source getSource( SourceConfiguration config, int bandwidth )
throws SourceException
{
Source retVal = null;
if( channel != null )
switch( config.getSourceType() )
{
switch( channel.getChannel()
.getSourceConfiguration().getSourceType() )
{
case MIXER:
retVal = MixerManager.getInstance().getSource( channel );
break;
case TUNER:
retVal = mResourceManager.getTunerManager().getSource( channel );
break;
case RECORDING:
retVal = mResourceManager.getRecordingSourceManager()
.getSource( channel );
case NONE:
default:
break;
}
case MIXER:
retVal = MixerManager.getInstance().getSource( config );
break;
case TUNER:
retVal = mResourceManager.getTunerManager().getSource( config, bandwidth );
break;
case RECORDING:
retVal = mResourceManager.getRecordingSourceManager()
.getSource( config, bandwidth );
case NONE:
default:
break;
}
return retVal;

View File

@@ -20,6 +20,8 @@ package source.config;
import javax.xml.bind.annotation.XmlAttribute;
import source.SourceType;
import source.tuner.TunerChannel;
import source.tuner.TunerChannel.Type;
public class SourceConfigRecording extends SourceConfiguration
{
@@ -58,4 +60,11 @@ public class SourceConfigRecording extends SourceConfiguration
{
return getRecordingAlias();
}
public TunerChannel getTunerChannel()
{
/* Create a tuner channel with a default bandwidth of 15 kHz */
return new TunerChannel( Type.LOCKED, mFrequency, 15000 );
}
}

View File

@@ -22,18 +22,29 @@ import java.text.DecimalFormat;
import javax.xml.bind.annotation.XmlAttribute;
import source.SourceType;
import source.tuner.TunerChannel;
import source.tuner.TunerChannel.Type;
public class SourceConfigTuner extends SourceConfiguration
{
private static DecimalFormat sFORMAT = new DecimalFormat( "0.00000" );
private long mFrequency = 0;
private int mBandwidth = 15000;
public SourceConfigTuner()
{
super( SourceType.TUNER );
}
public SourceConfigTuner( TunerChannel tunerChannel )
{
this();
mFrequency = tunerChannel.getFrequency();
mBandwidth = tunerChannel.getBandwidth();
}
@XmlAttribute( name = "frequency" )
public long getFrequency()
{
@@ -50,4 +61,9 @@ public class SourceConfigTuner extends SourceConfiguration
{
return sFORMAT.format( (double)mFrequency / 1000000.0d ) + " MHz";
}
public TunerChannel getTunerChannel()
{
return new TunerChannel( Type.LOCKED, mFrequency, mBandwidth );
}
}

View File

@@ -32,7 +32,7 @@ import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.adapter.SampleAdapter;
import sample.complex.ComplexBuffer;
import sample.complex.ComplexSample;
import sample.complex.Complex;
import source.SourceException;
public class ComplexMixer
@@ -102,15 +102,15 @@ public class ComplexMixer
mBufferReader.stop();
}
private List<ComplexSample> convert( ComplexBuffer sampleBuffer )
private List<Complex> convert( ComplexBuffer sampleBuffer )
{
float[] samples = sampleBuffer.getSamples();
ArrayList<ComplexSample> converted = new ArrayList<ComplexSample>();
ArrayList<Complex> converted = new ArrayList<Complex>();
for( int x = 0; x < samples.length; x += 2 )
{
converted.add( new ComplexSample( samples[ x ], samples[ x + 1 ] ) );
converted.add( new Complex( samples[ x ], samples[ x + 1 ] ) );
}
sampleBuffer.dispose();

View File

@@ -31,6 +31,8 @@ import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.TargetDataLine;
import module.ProcessingChainOld;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,7 +42,6 @@ import source.config.SourceConfigMixer;
import source.config.SourceConfiguration;
import source.tuner.MixerTunerDataLine;
import source.tuner.MixerTunerType;
import controller.channel.ProcessingChain;
public class MixerManager
{
@@ -82,13 +83,10 @@ public class MixerManager
return sInstance;
}
public RealMixerSource getSource( ProcessingChain processingChain )
public RealMixerSource getSource( SourceConfiguration config )
{
RealMixerSource retVal = null;
SourceConfiguration config =
processingChain.getChannel().getSourceConfiguration();
if( config instanceof SourceConfigMixer )
{
SourceConfigMixer mixerConfig = (SourceConfigMixer)config;

View File

@@ -29,7 +29,7 @@ import javax.sound.sampled.TargetDataLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dsp.gain.RealAutomaticGainControl;
import dsp.gain.AutomaticGainControl;
import sample.Listener;
import sample.adapter.SampleAdapter;
import sample.real.RealBuffer;
@@ -50,7 +50,7 @@ public class RealMixerSource extends RealSource
private String mDisplayName;
private int mBytesPerFrame = 0;
private SampleAdapter mSampleAdapter;
private RealAutomaticGainControl mAGC = new RealAutomaticGainControl();
private AutomaticGainControl mAGC = new AutomaticGainControl();
CopyOnWriteArrayList<Listener<RealBuffer>> mSampleListeners =
new CopyOnWriteArrayList<Listener<RealBuffer>>();

View File

@@ -17,8 +17,6 @@
******************************************************************************/
package source.recording;
import gui.SDRTrunk;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -30,10 +28,10 @@ import settings.SettingsManager;
import source.Source;
import source.SourceException;
import source.config.SourceConfigRecording;
import source.config.SourceConfiguration;
import source.tuner.TunerChannel;
import source.tuner.TunerChannelSource;
import controller.ResourceManager;
import controller.channel.ProcessingChain;
public class RecordingSourceManager
{
@@ -55,21 +53,21 @@ public class RecordingSourceManager
* Iterates current recordings to get a tuner channel source for the frequency
* specified in the channel config's source config object
*/
public Source getSource( ProcessingChain processingChain )
public Source getSource( SourceConfiguration config, int bandwidth )
throws SourceException
{
TunerChannelSource retVal = null;
if( processingChain.getChannel().getSourceConfiguration()
instanceof SourceConfigRecording )
if( config instanceof SourceConfigRecording )
{
TunerChannel tunerChannel =
processingChain.getChannel().getTunerChannel();
SourceConfigRecording configRecording = (SourceConfigRecording)config;
TunerChannel tunerChannel = configRecording.getTunerChannel();
tunerChannel.setBandwidth( bandwidth );
SourceConfigRecording config = (SourceConfigRecording)processingChain
.getChannel().getSourceConfiguration();
Recording recording = getRecordingFromAlias( config.getRecordingAlias() );
Recording recording = getRecordingFromAlias(
configRecording.getRecordingAlias() );
if( recording != null )
{
@@ -80,7 +78,7 @@ public class RecordingSourceManager
else
{
throw new SourceException( "Recording with alias name [" +
config.getRecordingAlias() + "] is not currently available" );
configRecording.getRecordingAlias() + "] is not currently available" );
}
}

View File

@@ -23,25 +23,25 @@ import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.Complex;
import sample.complex.ComplexBuffer;
import sample.complex.ComplexSample;
import sample.complex.ComplexSampleAssembler;
import source.ComplexSource;
import source.SourceException;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.FrequencyChangeListener;
import source.tuner.frequency.FrequencyChangeEvent.Attribute;
import source.tuner.frequency.FrequencyChangeListener;
import util.Oscillator;
import controller.ThreadPoolManager;
import controller.ThreadPoolManager.ThreadType;
import dsp.filter.ComplexPrimeCICDecimate;
import dsp.filter.FilterFactory;
import dsp.filter.Window.WindowType;
import dsp.filter.cic.ComplexPrimeCICDecimate;
public class TunerChannelSource extends ComplexSource
implements FrequencyChangeListener,
@@ -57,17 +57,18 @@ public class TunerChannelSource extends ComplexSource
new LinkedTransferQueue<ComplexBuffer>();
private Tuner mTuner;
private TunerChannel mTunerChannel;
private Oscillator mSineWaveGenerator;
private Oscillator mMixer;
private ComplexPrimeCICDecimate mDecimationFilter;
private ComplexSampleAssembler mBufferAssembler =
new ComplexSampleAssembler( 3000 );
private long mTunerFrequency = 0;
private int mTunerFrequencyError = 0;
private int mTunerSampleRate;
private Listener<ComplexBuffer> mListener;
private FrequencyChangeListener mFrequencyChangeListener;
private ThreadPoolManager mThreadPoolManager;
private ScheduledFuture<?> mTaskHandle;
private long mTunerFrequency = 0;
private int mTunerFrequencyError = 0;
private int mTunerSampleRate;
private DecimationProcessor mDecimationProcessor = new DecimationProcessor();
public TunerChannelSource( ThreadPoolManager threadPoolManager,
Tuner tuner,
@@ -77,7 +78,6 @@ public class TunerChannelSource extends ComplexSource
super( "Tuner Channel Source" );
mThreadPoolManager = threadPoolManager;
mTuner = tuner;
mTuner.addListener( (FrequencyChangeListener)this );
@@ -89,7 +89,7 @@ public class TunerChannelSource extends ComplexSource
/* Setup the frequency translator to the current source frequency */
long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency();
mSineWaveGenerator = new Oscillator( frequencyOffset, mTuner.getSampleRate() );
mMixer = new Oscillator( frequencyOffset, mTuner.getSampleRate() );
/* Fire a sample rate change event to get the decimation chain setup */
frequencyChanged( new FrequencyChangeEvent(
@@ -97,7 +97,7 @@ public class TunerChannelSource extends ComplexSource
/* Schedule the decimation task to run 50 times a second */
mTaskHandle = mThreadPoolManager.scheduleFixedRate( ThreadType.DECIMATION,
new DecimationProcessor(), 20, TimeUnit.MILLISECONDS );
mDecimationProcessor, 20, TimeUnit.MILLISECONDS );
/* Finally, register to receive samples from the tuner */
mTuner.addListener( (Listener<ComplexBuffer>)this );
@@ -106,31 +106,44 @@ public class TunerChannelSource extends ComplexSource
@Override
public void dispose()
{
mFrequencyChangeListener = null;
//Tell the tuner to release our resources
/* Tell the tuner to release/unregister our resources */
mTuner.removeListener( (FrequencyChangeListener)this );
mTuner.releaseChannel( this );
mTuner = null;
mTunerChannel = null;
/* Flag the decimation processor to shutdown */
if( mDecimationProcessor != null )
{
mDecimationProcessor.dispose();
}
}
/**
* Cleanup method to allow the decimation processor to perform final cleanup
* once it gracefully shuts itself down
*/
private void cleanup()
{
if( mTaskHandle != null )
{
mThreadPoolManager.cancel( mTaskHandle );
mThreadPoolManager = null;
mTaskHandle = null;
}
mBuffer.clear();
mBuffer = null;
// mDecimationFilter.dispose();
mFrequencyChangeListener = null;
mListener = null;
mMixer = null;
mDecimationFilter.dispose();
mDecimationFilter = null;
mBufferAssembler.dispose();
mTuner = null;
mTunerChannel = null;
mTunerChannel = null;
mTuner = null;
}
public Tuner getTuner()
@@ -144,22 +157,31 @@ public class TunerChannelSource extends ComplexSource
}
@Override
public void receive( ComplexBuffer sampleArray )
public void receive( ComplexBuffer buffer )
{
mBuffer.add( sampleArray );
mBuffer.add( buffer );
}
public void setFrequencyChangeListener( FrequencyChangeListener listener )
{
mFrequencyChangeListener = listener;
}
@Override
public void setListener( Listener<List<ComplexSample>> listener )
{
mBufferAssembler.setListener( listener );
}
public void setListener( Listener<ComplexBuffer> listener )
{
/* Get a pointer to the listener so that if we have to change the
* decimation filter, we can re-add the listener */
mListener = listener;
mDecimationFilter.setListener( listener );
}
@Override
public void removeListener( Listener<List<ComplexSample>> listener )
{
mBufferAssembler.removeListener( listener );
}
public void removeListener( Listener<ComplexBuffer> listener )
{
mDecimationFilter.removeListener();
}
@Override
public void frequencyChanged( FrequencyChangeEvent event )
@@ -180,7 +202,7 @@ public class TunerChannelSource extends ComplexSource
if( mTunerSampleRate != sampleRate )
{
/* Set the oscillator to the new frequency */
mSineWaveGenerator.setSampleRate( sampleRate );
mMixer.setSampleRate( sampleRate );
/* Get new decimation filter */
mDecimationFilter = FilterFactory
@@ -192,7 +214,7 @@ public class TunerChannelSource extends ComplexSource
WindowType.HAMMING );
/* re-add the original output listener */
mDecimationFilter.setListener( mBufferAssembler );
mDecimationFilter.setListener( mListener );
mTunerSampleRate = sampleRate;
}
@@ -204,7 +226,7 @@ public class TunerChannelSource extends ComplexSource
mTunerChannel.getFrequency() -
mTunerFrequencyError;
mSineWaveGenerator.setFrequency( frequencyErrorOffset );
mMixer.setFrequency( frequencyErrorOffset );
break;
case FREQUENCY:
long frequency = event.getValue().longValue();
@@ -215,7 +237,7 @@ public class TunerChannelSource extends ComplexSource
long frequencyOffset = frequency - mTunerChannel.getFrequency();
mSineWaveGenerator.setFrequency( frequencyOffset );
mMixer.setFrequency( frequencyOffset );
mTunerFrequency = frequency;
break;
@@ -226,16 +248,27 @@ public class TunerChannelSource extends ComplexSource
public int getSampleRate() throws SourceException
{
return mTuner.getSampleRate();
return CHANNEL_RATE;
}
public long getFrequency() throws SourceException
{
return mTuner.getFrequency();
return mTunerChannel.getFrequency();
}
/**
* Decimates an inbound buffer of I/Q samples from the source down to the
* standard 48000 channel sample rate
*/
public class DecimationProcessor implements Runnable
{
private AtomicBoolean mDispose = new AtomicBoolean( false );
public void dispose()
{
mDispose.set( true );
}
@Override
public void run()
{
@@ -244,38 +277,59 @@ public class TunerChannelSource extends ComplexSource
* run the program out of memory */
try
{
List<ComplexBuffer> sampleBuffers =
new ArrayList<ComplexBuffer>();
if( mBuffer != null )
/* Check to see if we've been shutdown */
if( mDispose.get() )
{
/* Limit to 4, so that after a garbage collect we don't
* induce wild swings as it tries to take tons of samples
* and process them, causing delays and buffer refill */
mBuffer.drainTo( sampleBuffers, 4 );
for( ComplexBuffer buffer: sampleBuffers )
cleanup();
return;
}
else
{
List<ComplexBuffer> sampleBuffers =
new ArrayList<ComplexBuffer>();
if( mBuffer != null )
{
float[] samples = buffer.getSamples();
float[] translated = new float[ samples.length ];
/* Perform frequency translation */
for( int x = 0; x < samples.length; x += 2 )
mBuffer.drainTo( sampleBuffers, 4 );
for( ComplexBuffer buffer: sampleBuffers )
{
ComplexSample multiplier =
mSineWaveGenerator.nextComplex();
translated[ x ] = ( samples[ x ] * multiplier.inphase() ) -
( samples[ x + 1 ] * multiplier.quadrature() );
translated[ x + 1 ] = ( samples[ x + 1 ] * multiplier.inphase() ) +
( samples[ x ] * multiplier.quadrature() );
/* Check to see if we've been shutdown */
if( mDispose.get() )
{
cleanup();
return;
}
else
{
float[] samples = buffer.getSamples();
/* We make a copy of the buffer so that we don't affect
* anyone else that is using the same buffer, like other
* channels or the spectral display */
float[] translated = new float[ samples.length ];
/* Perform frequency translation */
for( int x = 0; x < samples.length; x += 2 )
{
mMixer.rotate();
translated[ x ] = Complex.multiplyInphase(
samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() );
translated[ x + 1 ] = Complex.multiplyQuadrature(
samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() );
}
if( mDecimationFilter != null )
{
mDecimationFilter.receive( new ComplexBuffer( translated ) );
}
}
}
mDecimationFilter.receive( translated );
sampleBuffers.clear();
}
sampleBuffers.clear();
}
}
catch( Exception e )
@@ -284,9 +338,4 @@ public class TunerChannelSource extends ComplexSource
}
}
}
public void addListener( FrequencyChangeListener listener )
{
mFrequencyChangeListener = listener;
}
}

View File

@@ -156,9 +156,8 @@ public abstract class TunerController implements Tunable
}
public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager,
Tuner tuner, TunerChannel tunerChannel )
throws RejectedExecutionException,
SourceException
Tuner tuner, TunerChannel tunerChannel )
throws RejectedExecutionException, SourceException
{
TunerChannelSource source = null;

View File

@@ -33,6 +33,7 @@ import org.usb4java.LibUsb;
import source.Source;
import source.SourceException;
import source.config.SourceConfigTuner;
import source.config.SourceConfiguration;
import source.mixer.MixerManager;
import source.tuner.fcd.FCDTuner;
import source.tuner.fcd.proV1.FCD1TunerController;
@@ -44,7 +45,6 @@ import source.tuner.rtl.RTL2832TunerController;
import source.tuner.rtl.e4k.E4KTunerController;
import source.tuner.rtl.r820t.R820TTunerController;
import controller.ResourceManager;
import controller.channel.ProcessingChain;
public class TunerManager
{
@@ -76,15 +76,17 @@ public class TunerManager
* Iterates current tuners to get a tuner channel source for the frequency
* specified in the channel config's source config object
*/
public Source getSource( ProcessingChain processingChain )
public Source getSource( SourceConfiguration config, int bandwidth )
{
TunerChannelSource retVal = null;
if( processingChain.getChannel().getSourceConfiguration()
instanceof SourceConfigTuner )
if( config instanceof SourceConfigTuner )
{
TunerChannel tunerChannel = processingChain.getChannel().getTunerChannel();
SourceConfigTuner tunerConfig = (SourceConfigTuner)config;
TunerChannel tunerChannel = tunerConfig.getTunerChannel();
tunerChannel.setBandwidth( bandwidth );
Iterator<Tuner> it = mTuners.iterator();
@@ -358,7 +360,8 @@ public class TunerManager
try
{
HackRFTunerController hackRFController =
new HackRFTunerController( device, descriptor );
new HackRFTunerController( device, descriptor,
mResourceManager.getThreadPoolManager() );
hackRFController.init();
@@ -409,7 +412,8 @@ public class TunerManager
try
{
E4KTunerController controller =
new E4KTunerController( device, deviceDescriptor );
new E4KTunerController( device, deviceDescriptor,
mResourceManager.getThreadPoolManager() );
controller.init();
@@ -432,14 +436,12 @@ public class TunerManager
+ "controller - " + se.getLocalizedMessage() );
}
case RAFAELMICRO_R820T:
mLog.debug( "attempting to construct R820T "
+ "tuner controller" );
try
{
R820TTunerController controller =
new R820TTunerController( device, deviceDescriptor );
new R820TTunerController( device, deviceDescriptor,
mResourceManager.getThreadPoolManager() );
mLog.debug( "initializing R820T tuner controller" );
controller.init();
RTL2832Tuner rtlTuner =
@@ -450,14 +452,8 @@ public class TunerManager
if( config != null )
{
mLog.debug( "applying tuner config to R820T tuner" );
rtlTuner.apply( config );
}
else
{
mLog.debug( "R820T tuner config was "
+ "null - not applied" );
}
return new TunerInitStatus( rtlTuner, "LOADED" );
}

View File

@@ -25,11 +25,11 @@ import sample.real.RealSampleListener;
import source.tuner.frequency.FrequencyChangeEvent.Attribute;
import buffer.FloatAveragingBuffer;
public class AutomaticFrequencyControl extends FrequencyCorrectionControl
public class AutomaticFrequencyControl_R extends FrequencyCorrectionControl
implements RealSampleListener
{
private final static Logger mLog =
LoggerFactory.getLogger( AutomaticFrequencyControl.class );
LoggerFactory.getLogger( AutomaticFrequencyControl_R.class );
private static float LARGE_ERROR = 0.300f;
private static float MEDIUM_ERROR = 0.150f;
@@ -64,7 +64,7 @@ public class AutomaticFrequencyControl extends FrequencyCorrectionControl
*
* @param maximum - maximum allowable (+/-) frequency correction value.
*/
public AutomaticFrequencyControl( int maximum )
public AutomaticFrequencyControl_R( int maximum )
{
super( maximum );
}

View File

@@ -0,0 +1,197 @@
package source.tuner.frequency;
/*******************************************************************************
* SDR Trunk
* Copyright (C) 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/>
******************************************************************************/
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.real.RealBuffer;
import source.tuner.frequency.FrequencyChangeEvent.Attribute;
import buffer.FloatAveragingBuffer;
public class AutomaticFrequencyControl_RB extends FrequencyCorrectionControl
implements Listener<RealBuffer>
{
private final static Logger mLog =
LoggerFactory.getLogger( AutomaticFrequencyControl_RB.class );
private static float LARGE_ERROR = 0.300f;
private static float MEDIUM_ERROR = 0.150f;
private static float SMALL_ERROR = 0.030f;
private static float FINE_ERROR = 0.015f;
private static int LARGE_FREQUENCY_CORRECTION = 1500; //Hertz
private static int MEDIUM_FREQUENCY_CORRECTION = 500;
private static int SMALL_FREQUENCY_CORRECTION = 100;
private static int FINE_FREQUENCY_CORRECTION = 50;
private static int SAMPLE_THRESHOLD = 15000;
private Mode mMode = Mode.FAST;
private FloatAveragingBuffer mBuffer =
new FloatAveragingBuffer( SAMPLE_THRESHOLD );
private int mSampleCounter = 0;
private int mSkipCounter = 0;
private int mSkipThreshold = 4;
private int mErrorCorrection = 0;
public enum Mode
{
NORMAL,FAST;
}
/**
* Automatic Frequency Control. Monitors DC bias present in an FM
* demodulated audio stream and issues automatic frequency corrections to
* maintain the DC bias close to zero.
*
* @param maximum - maximum allowable (+/-) frequency correction value.
*/
public AutomaticFrequencyControl_RB( int maximum )
{
super( maximum );
}
public void dispose()
{
super.dispose();
mBuffer = null;
}
/**
* Resets the error correction value to zero and broadcasts a change event
*/
public void reset()
{
super.reset();
mMode = Mode.FAST;
dispatch();
}
/**
* Broadcasts a frequency correction change event to the registered listener
*/
private void dispatch()
{
if( mListener != null )
{
mListener.frequencyChanged(
new FrequencyChangeEvent( Attribute.FREQUENCY_ERROR,
mErrorCorrection ) );
}
}
@Override
public void receive( RealBuffer t )
{
float average = 0.0f;
for( float sample: t.getSamples() )
{
average = mBuffer.get( sample );
}
//TODO: fix this
// update( average );
}
public void receive( float sample )
{
if( mMode == Mode.FAST )
{
float average = mBuffer.get( sample );
mSampleCounter++;
if( mSampleCounter >= SAMPLE_THRESHOLD )
{
mSampleCounter = 0;
update( average );
}
}
else
{
mSkipCounter++;
if( mSkipCounter >= mSkipThreshold )
{
mSkipCounter = 0;
float average = mBuffer.get( sample );
mSampleCounter++;
if( mSampleCounter >= SAMPLE_THRESHOLD )
{
mSampleCounter = 0;
update( average );
}
}
}
}
private void update( float average )
{
mMode = Mode.NORMAL;
if( average > LARGE_ERROR )
{
adjust( -LARGE_FREQUENCY_CORRECTION );
mMode = Mode.FAST;
}
else if( average > MEDIUM_ERROR )
{
adjust( -MEDIUM_FREQUENCY_CORRECTION );
mMode = Mode.FAST;
}
else if( average > SMALL_ERROR )
{
adjust( -SMALL_FREQUENCY_CORRECTION );
}
else if( average > FINE_ERROR )
{
adjust( -FINE_FREQUENCY_CORRECTION );
}
else if( average < -LARGE_ERROR )
{
adjust( LARGE_FREQUENCY_CORRECTION );
mMode = Mode.FAST;
}
else if( average < -MEDIUM_ERROR )
{
adjust( MEDIUM_FREQUENCY_CORRECTION );
mMode = Mode.FAST;
}
else if( average < -SMALL_ERROR )
{
adjust( SMALL_FREQUENCY_CORRECTION );
}
else if( average < -FINE_ERROR )
{
adjust( FINE_FREQUENCY_CORRECTION );
}
}
}

View File

@@ -59,7 +59,7 @@ public class FrequencyCorrectionControl implements FrequencyChangeListener
/**
* Registers a listener to receive frequency correction events
*/
public void setListener( FrequencyChangeListener listener )
public void setFrequencyChangeListener( FrequencyChangeListener listener )
{
mListener = listener;
}
@@ -92,7 +92,7 @@ public class FrequencyCorrectionControl implements FrequencyChangeListener
* Resets frequency correction value to zero and notifies any reset
* listeners
*/
protected void reset()
public void reset()
{
setErrorCorrection( 0, false );

View File

@@ -0,0 +1,8 @@
package source.tuner.frequency;
public interface IFrequencyCorrectionController
{
public boolean hasFrequencyCorrectionControl();
public FrequencyCorrectionControl getFrequencyCorrectionControl();
}

View File

@@ -58,9 +58,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -78,12 +76,15 @@ import org.usb4java.LibUsbException;
import org.usb4java.Transfer;
import org.usb4java.TransferCallback;
import sample.Broadcaster;
import sample.Listener;
import sample.adapter.ByteSampleAdapter;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.TunerConfiguration;
import source.tuner.TunerController;
import controller.ThreadPoolManager;
import controller.ThreadPoolManager.ThreadType;
public class HackRFTunerController extends TunerController
@@ -114,8 +115,7 @@ public class HackRFTunerController extends TunerController
private LinkedTransferQueue<byte[]> mFilledBuffers =
new LinkedTransferQueue<byte[]>();
private CopyOnWriteArrayList<Listener<ComplexBuffer>> mSampleListeners =
new CopyOnWriteArrayList<Listener<ComplexBuffer>>();
private Broadcaster<ComplexBuffer> mComplexBufferBroadcaster = new Broadcaster<>();
private ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter();
private BufferProcessor mBufferProcessor = new BufferProcessor();
@@ -127,14 +127,16 @@ public class HackRFTunerController extends TunerController
private Device mDevice;
private DeviceDescriptor mDeviceDescriptor;
private DeviceHandle mDeviceHandle;
private ThreadPoolManager mThreadPoolManager;
public HackRFTunerController( Device device, DeviceDescriptor descriptor )
throws SourceException
public HackRFTunerController( Device device, DeviceDescriptor descriptor,
ThreadPoolManager threadPoolManager ) throws SourceException
{
super( MIN_FREQUENCY, MAX_FREQUENCY );
mDevice = device;
mDeviceDescriptor = descriptor;
mThreadPoolManager = threadPoolManager;
}
public void init() throws SourceException
@@ -870,7 +872,7 @@ public class HackRFTunerController extends TunerController
*/
public void addListener( Listener<ComplexBuffer> listener )
{
mSampleListeners.add( listener );
mComplexBufferBroadcaster.addListener( listener );
if( mBufferProcessor == null || !mBufferProcessor.isRunning() )
{
@@ -890,51 +892,20 @@ public class HackRFTunerController extends TunerController
*/
public void removeListener( Listener<ComplexBuffer> listener )
{
mSampleListeners.remove( listener );
if( mSampleListeners.isEmpty() )
mComplexBufferBroadcaster.removeListener( listener );
if( !mComplexBufferBroadcaster.hasListeners() )
{
mBufferProcessor.stop();
}
}
/**
* Dispatches the sample array to each registered listener. If there is more
* than one listener, they receive a copy of the samples. If there is only
* one listener, or the last listener, they receive the original sample array.
*
* This is to facilitate garbage collection of the array when the listener
* is done processing the samples.
*/
public void broadcast( ComplexBuffer samples )
{
Iterator<Listener<ComplexBuffer>> it = mSampleListeners.iterator();
while( it.hasNext() )
{
Listener<ComplexBuffer> next = it.next();
/* if this is the last (or only) listener, send the original
* buffer, otherwise send a copy of the buffer */
if( it.hasNext() )
{
next.receive( samples.copyOf() );
}
else
{
next.receive( samples );
}
}
}
/**
* Buffer processing thread. Fetches samples from the HackRF Tuner and
* dispatches them to all registered listeners
*/
public class BufferProcessor implements Runnable, TransferCallback
{
private ScheduledExecutorService mExecutor =
Executors.newScheduledThreadPool( 1 );
private ScheduledFuture<?> mSampleDispatcherTask;
private CopyOnWriteArrayList<Transfer> mTransfers;
private AtomicBoolean mRunning = new AtomicBoolean();
@@ -960,9 +931,9 @@ public class HackRFTunerController extends TunerController
}
}
mSampleDispatcherTask = mExecutor
.scheduleAtFixedRate( new BufferDispatcher(),
0, 20, TimeUnit.MILLISECONDS );
mSampleDispatcherTask = mThreadPoolManager.scheduleFixedRate(
ThreadType.SOURCE_SAMPLE_PROCESSING, new BufferDispatcher(),
20, TimeUnit.MILLISECONDS );
while( mRunning.get() )
{
@@ -1125,7 +1096,7 @@ public class HackRFTunerController extends TunerController
{
float[] samples = mSampleAdapter.convert( buffer );
broadcast( new ComplexBuffer( samples ) );
mComplexBufferBroadcaster.broadcast( new ComplexBuffer( samples ) );
}
}
catch( Exception e )

View File

@@ -29,9 +29,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -51,16 +49,20 @@ import org.usb4java.LibUsbException;
import org.usb4java.Transfer;
import org.usb4java.TransferCallback;
import sample.Broadcaster;
import sample.Listener;
import sample.adapter.ByteSampleAdapter;
import sample.complex.ComplexBuffer;
import sample.complex.Complex;
import source.SourceException;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.FrequencyChangeEvent.Attribute;
import source.tuner.TunerController;
import source.tuner.TunerType;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.FrequencyChangeEvent.Attribute;
import buffer.FloatAveragingBuffer;
import controller.ResourceManager;
import controller.ThreadPoolManager;
import controller.ThreadPoolManager.ThreadType;
public abstract class RTL2832TunerController extends TunerController
{
@@ -104,12 +106,11 @@ public abstract class RTL2832TunerController extends TunerController
private SampleRate mSampleRate = DEFAULT_SAMPLE_RATE;
private BufferProcessor mBufferProcessor = new BufferProcessor();
private BufferProcessor mBufferProcessor;
private ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter();
private CopyOnWriteArrayList<Listener<ComplexBuffer>> mSampleListeners =
new CopyOnWriteArrayList<Listener<ComplexBuffer>>();
private Broadcaster<ComplexBuffer> mComplexBufferBroadcaster = new Broadcaster<>();
private LinkedTransferQueue<byte[]> mFilledBuffers =
new LinkedTransferQueue<byte[]>();
@@ -127,6 +128,7 @@ public abstract class RTL2832TunerController extends TunerController
public int mBufferSize = 131072;
protected Descriptor mDescriptor;
private ThreadPoolManager mThreadPoolManager;
/**
* Abstract tuner controller device. Use the static getTunerClass() method
@@ -135,11 +137,14 @@ public abstract class RTL2832TunerController extends TunerController
*/
public RTL2832TunerController( Device device,
DeviceDescriptor deviceDescriptor,
ThreadPoolManager threadPoolManager,
long minTunableFrequency,
long maxTunableFrequency ) throws SourceException
{
super( minTunableFrequency, maxTunableFrequency );
mThreadPoolManager = threadPoolManager;
mDevice = device;
mDeviceDescriptor = deviceDescriptor;
}
@@ -1345,12 +1350,15 @@ public abstract class RTL2832TunerController extends TunerController
*/
public void addListener( Listener<ComplexBuffer> listener )
{
mSampleListeners.add( listener );
mComplexBufferBroadcaster.addListener( listener );
if( mBufferProcessor == null || !mBufferProcessor.isRunning() )
if( mBufferProcessor == null )
{
mBufferProcessor = new BufferProcessor( mThreadPoolManager );
}
if( !mBufferProcessor.isRunning() )
{
mBufferProcessor = new BufferProcessor();
Thread thread = new Thread( mBufferProcessor );
thread.setDaemon( true );
thread.setName( "RTL2832 Sample Processor" );
@@ -1365,59 +1373,35 @@ public abstract class RTL2832TunerController extends TunerController
*/
public void removeListener( Listener<ComplexBuffer> listener )
{
mSampleListeners.remove( listener );
if( mSampleListeners.isEmpty() )
mComplexBufferBroadcaster.removeListener( listener );
if( !mComplexBufferBroadcaster.hasListeners() )
{
mBufferProcessor.stop();
}
}
/**
* Dispatches float sample buffers to all registered listeners
*/
public void broadcast( ComplexBuffer buffer )
{
Iterator<Listener<ComplexBuffer>> it = mSampleListeners.iterator();
while( it.hasNext() )
{
Listener<ComplexBuffer> next = it.next();
/* if this is the last (or only) listener, send him the original
* buffer, otherwise send him a copy of the buffer */
if( it.hasNext() )
{
next.receive( buffer.copyOf() );
}
else
{
next.receive( buffer );
}
}
}
/**
* Buffer processing thread. Fetches samples from the RTL2832 Tuner and
* dispatches them to all registered listeners
*/
public class BufferProcessor implements Runnable, TransferCallback
{
private ScheduledExecutorService mExecutor =
Executors.newScheduledThreadPool( 2 );
private ScheduledFuture<?> mSampleDispatcherTask;
private ScheduledFuture<?> mSampleRateCounterTask;
private CopyOnWriteArrayList<Transfer> mTransfers;
private AtomicBoolean mRunning = new AtomicBoolean();
private ThreadPoolManager mThreadPoolManager;
public BufferProcessor( ThreadPoolManager manager )
{
mThreadPoolManager = manager;
}
@Override
public void run()
{
if( mRunning.compareAndSet( false, true ) )
{
mLog.debug( "rtl2832 [" + getUniqueID() +
"] - starting sample fetch thread" );
try
{
setSampleRate( mSampleRate );
@@ -1445,16 +1429,17 @@ public abstract class RTL2832TunerController extends TunerController
}
}
mSampleDispatcherTask = mExecutor
.scheduleAtFixedRate( new BufferDispatcher(),
0, 20, TimeUnit.MILLISECONDS );
mSampleRateMonitor =
new SampleRateMonitor( mSampleRate.getRate() );
mSampleRateCounterTask = mExecutor
.scheduleAtFixedRate( mSampleRateMonitor,
10, 10, TimeUnit.SECONDS );
try
{
mSampleDispatcherTask = mThreadPoolManager.scheduleFixedRate(
ThreadType.SOURCE_SAMPLE_PROCESSING, new BufferDispatcher(),
20, TimeUnit.MILLISECONDS );
}
catch( NullPointerException npe )
{
mLog.error( "NPE! = tpm null: " + ( mThreadPoolManager == null ), npe );
}
while( mRunning.get() )
{
@@ -1478,15 +1463,12 @@ public abstract class RTL2832TunerController extends TunerController
{
if( mRunning.compareAndSet( true, false ) )
{
mLog.debug( "rtl2832 [" + getUniqueID() +
"] - stopping sample fetch thread" );
cancel();
if( mSampleDispatcherTask != null )
{
mSampleDispatcherTask.cancel( true );
mSampleRateCounterTask.cancel( true );
mThreadPoolManager.cancel( mSampleDispatcherTask );
mSampleDispatcherTask = null;
mFilledBuffers.clear();
}
}
@@ -1621,7 +1603,7 @@ public abstract class RTL2832TunerController extends TunerController
{
float[] samples = mSampleAdapter.convert( buffer );
broadcast( new ComplexBuffer( samples ) );
mComplexBufferBroadcaster.broadcast( new ComplexBuffer( samples ) );
}
}
catch( Exception e )

View File

@@ -34,8 +34,8 @@ import source.SourceException;
import source.tuner.TunerConfiguration;
import source.tuner.TunerType;
import source.tuner.rtl.RTL2832TunerController;
import source.tuner.rtl.RTL2832TunerController.Descriptor;
import controller.ResourceManager;
import controller.ThreadPoolManager;
public class E4KTunerController extends RTL2832TunerController
{
@@ -111,10 +111,12 @@ public class E4KTunerController extends RTL2832TunerController
private E4KTunerEditorPanel mEditor;
public E4KTunerController( Device device,
DeviceDescriptor deviceDescriptor )
DeviceDescriptor deviceDescriptor,
ThreadPoolManager threadPoolManager )
throws SourceException
{
super( device, deviceDescriptor, MIN_FREQUENCY, MAX_FREQUENCY );
super( device, deviceDescriptor, threadPoolManager,
MIN_FREQUENCY, MAX_FREQUENCY );
}
@Override

View File

@@ -270,64 +270,64 @@ public class R820TTunerConfigurationPanel extends JPanel
add( new JLabel( "Sample Rate:" ) );
add( mComboSampleRate, "growx,push" );
/**
* Sample Rate Correction
*/
SpinnerModel sampleRateCorrectionModel =
new SpinnerNumberModel( 0, //initial value
-32768, //min
32767, //max
1 ); //step
mSampleRateCorrection = new JSpinner( sampleRateCorrectionModel );
JSpinner.NumberEditor sampleRateCorrectionEditor =
(JSpinner.NumberEditor)mSampleRateCorrection.getEditor();
DecimalFormat sampleRateCorrectionFormat =
sampleRateCorrectionEditor.getFormat();
sampleRateCorrectionFormat.setMinimumFractionDigits( 0 );
sampleRateCorrectionEditor.getTextField()
.setHorizontalAlignment( SwingConstants.CENTER );
mSampleRateCorrection.addChangeListener( new ChangeListener()
{
@Override
public void stateChanged( ChangeEvent e )
{
final int value = ((SpinnerNumberModel)mSampleRateCorrection
.getModel()).getNumber().intValue();
EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
try
{
mController.setSampleRateFrequencyCorrection( value );
}
catch ( SourceException | LibUsbException e1 )
{
JOptionPane.showMessageDialog(
R820TTunerConfigurationPanel.this,
"E4K Tuner Controller - couldn't apply "
+ "sample rate correction value: " + value +
e1.getLocalizedMessage() );
mLog.error( "E4K Tuner Controller - couldn't apply "
+ "sample rate correction value: " + value +
e1.getLocalizedMessage() );
}
}
} );
}
} );
add( mSampleRateCorrection );
add( new JLabel( "Sample Rate Correction (ppm)" ), "grow,push" );
// /**
// * Sample Rate Correction
// */
// SpinnerModel sampleRateCorrectionModel =
// new SpinnerNumberModel( 0, //initial value
// -32768, //min
// 32767, //max
// 1 ); //step
//
// mSampleRateCorrection = new JSpinner( sampleRateCorrectionModel );
//
// JSpinner.NumberEditor sampleRateCorrectionEditor =
// (JSpinner.NumberEditor)mSampleRateCorrection.getEditor();
//
// DecimalFormat sampleRateCorrectionFormat =
// sampleRateCorrectionEditor.getFormat();
//
// sampleRateCorrectionFormat.setMinimumFractionDigits( 0 );
//
// sampleRateCorrectionEditor.getTextField()
// .setHorizontalAlignment( SwingConstants.CENTER );
//
// mSampleRateCorrection.addChangeListener( new ChangeListener()
// {
// @Override
// public void stateChanged( ChangeEvent e )
// {
// final int value = ((SpinnerNumberModel)mSampleRateCorrection
// .getModel()).getNumber().intValue();
//
// EventQueue.invokeLater( new Runnable()
// {
// @Override
// public void run()
// {
// try
// {
// mController.setSampleRateFrequencyCorrection( value );
// }
// catch ( SourceException | LibUsbException e1 )
// {
// JOptionPane.showMessageDialog(
// R820TTunerConfigurationPanel.this,
// "E4K Tuner Controller - couldn't apply "
// + "sample rate correction value: " + value +
// e1.getLocalizedMessage() );
//
// mLog.error( "E4K Tuner Controller - couldn't apply "
// + "sample rate correction value: " + value +
// e1.getLocalizedMessage() );
// }
// }
// } );
// }
// } );
//
// add( mSampleRateCorrection );
// add( new JLabel( "Sample Rate Correction (ppm)" ), "grow,push" );
/**
* Gain Controls

View File

@@ -38,6 +38,7 @@ import source.tuner.TunerConfiguration;
import source.tuner.TunerType;
import source.tuner.rtl.RTL2832TunerController;
import controller.ResourceManager;
import controller.ThreadPoolManager;
public class R820TTunerController extends RTL2832TunerController
{
@@ -73,10 +74,12 @@ public class R820TTunerController extends RTL2832TunerController
private JPanel mEditor;
public R820TTunerController( Device device,
DeviceDescriptor deviceDescriptor )
throws SourceException
DeviceDescriptor deviceDescriptor,
ThreadPoolManager threadPoolManager )
throws SourceException
{
super( device, deviceDescriptor, MIN_FREQUENCY, MAX_FREQUENCY );
super( device, deviceDescriptor, threadPoolManager,
MIN_FREQUENCY, MAX_FREQUENCY );
}
@Override
@@ -250,21 +253,15 @@ public class R820TTunerController extends RTL2832TunerController
try
{
mLog.debug( "initializing RTL2832 tuner baseband" );
initBaseband( mDeviceHandle );
mLog.debug( "enabling I2C repeater" );
enableI2CRepeater( mDeviceHandle, true );
boolean i2CRepeaterControl = false;
mLog.debug( "initializing R820T tuner" );
initTuner( i2CRepeaterControl );
mLog.debug( "disabling I2C repeater" );
enableI2CRepeater( mDeviceHandle, false );
mLog.debug( "initializing RTL2832 tuner controller super class" );
}
catch( UsbException e )
{

View File

@@ -35,7 +35,8 @@ import org.slf4j.LoggerFactory;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexSample;
import sample.SampleType;
import sample.complex.Complex;
public class ComplexWaveSource extends WaveSource
{
@@ -56,7 +57,7 @@ public class ComplexWaveSource extends WaveSource
private static int BUFFER_SAMPLE_SIZE = 2000;
private Broadcaster<ComplexSample> mBroadcaster = new Broadcaster<ComplexSample>();
private Broadcaster<Complex> mBroadcaster = new Broadcaster<Complex>();
/**
* Single channel (mono) wave file playback source with optional looping
@@ -227,7 +228,7 @@ public class ComplexWaveSource extends WaveSource
if( broadcast )
{
send( new ComplexSample( (float)i / 32767.0f, (float)q / 32767.0f ) );
send( new Complex( (float)i / 32767.0f, (float)q / 32767.0f ) );
}
success = true;
@@ -278,7 +279,7 @@ public class ComplexWaveSource extends WaveSource
/**
* Broadcasts a sample to the registered listeners
*/
private void send( ComplexSample sample )
private void send( Complex sample )
{
mBroadcaster.broadcast( sample );
}
@@ -286,12 +287,12 @@ public class ComplexWaveSource extends WaveSource
/**
* Adds a new listener to receive samples as they are read from the wave file
*/
public void addListener( Listener<ComplexSample> listener )
public void addListener( Listener<Complex> listener )
{
mBroadcaster.addListener( listener );
}
public void removeListener( Listener<ComplexSample> listener )
public void removeListener( Listener<Complex> listener )
{
mBroadcaster.removeListener( listener );
}

View File

@@ -29,6 +29,7 @@ import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import sample.SampleType;
import sample.adapter.ShortToFloatMap;
import sample.real.RealSampleListener;

View File

@@ -20,6 +20,7 @@ package source.wave;
import java.io.File;
import java.io.IOException;
import sample.SampleType;
import source.Source;
public abstract class WaveSource extends Source