Memory/CPU performance improvement and Audio/Mixer source restructuring

Converted to use float primitives and to keep samples in buffer format until the sample rate is decimated to 48kHz.  
Added new non-recursive, CIC decimation filter -- untested

Updated sound card/mixer input source management.  Now testing for mono/stereo support for each card and limiting channel choices accordingly.  Separated the mixersource into a ComplexMixer and and SimplexMixerSource.  The ComplexMixer is used for sound card based tuners like the funcube.  The SimplexMixerSource is used for demodulated sound card inputs.

Interfaces for sample handling - standardized on using 'Complex' to indicate I/Q samples and 'Simplex' to indicate demodulated float samples.  The processing chain now accepts either a decimated (48k) list of complex samples from a tuner channel source, or a 48k simplex buffer (array of demodulated float samples).

Resolved issue with HackRF tuner not applying tuner configuration information.
This commit is contained in:
dennis.sheirer
2014-10-26 10:11:30 +00:00
parent 0ad710fea2
commit e61c3a891c
71 changed files with 1754 additions and 619 deletions

View File

@@ -1,12 +0,0 @@
package source;
import sample.Provider;
import source.Source.SampleType;
public abstract class FloatArraySource extends Source implements Provider<Float[]>
{
public FloatArraySource( String name )
{
super( name, SampleType.FLOAT );
}
}

View File

@@ -0,0 +1,13 @@
package source;
import sample.Provider;
import sample.simplex.SimplexBuffer;
public abstract class SimplexSource extends Source implements Provider<SimplexBuffer>
{
public SimplexSource( String name )
{
super( name, SampleType.FLOAT );
}
}

View File

@@ -1,85 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.mixer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import source.Source.SampleType;
/**
* Converts 16-bit little endian byte data into an array of floats
*/
public class ChannelShortAdapter extends SampleAdapter
{
private ByteOrder mByteOrder = ByteOrder.LITTLE_ENDIAN;
private MixerChannel mMixerChannel;
public ChannelShortAdapter( MixerChannel channel )
{
/* Only use with LEFT/RIGHT channels */
assert( channel != MixerChannel.MONO );
mMixerChannel = channel;
}
@Override
public Float[] convert( byte[] samples )
{
Float[] processed = new Float[ samples.length / 4 ];
int pointer = 0;
/* Wrap byte array in a byte buffer so we can process them as shorts */
ByteBuffer buffer = ByteBuffer.wrap( samples );
/* Set endian to correct byte ordering */
buffer.order( mByteOrder );
while( buffer.hasRemaining() )
{
if( mMixerChannel == MixerChannel.LEFT )
{
processed[ pointer ] = (float)buffer.getShort();
/* Throw away the right channel */
buffer.getShort();
}
else
{
/* Throw away the left channel */
buffer.getShort();
processed[ pointer ] = (float)buffer.getShort();
}
pointer++;
}
return processed;
}
/**
* Set byte interpretation to little or big endian. Defaults to LITTLE
* endian
*/
public void setByteOrder( ByteOrder order )
{
mByteOrder = order;
}
}

View File

@@ -0,0 +1,273 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.mixer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.adapter.SampleAdapter;
import sample.complex.ComplexBuffer;
import sample.complex.ComplexSample;
import source.SourceException;
public class ComplexMixer
{
private final static Logger mLog =
LoggerFactory.getLogger( ComplexMixer.class );
private long mFrequency = 0;
private int mBufferSize = 16384;
private BufferReader mBufferReader = new BufferReader();
private TargetDataLine mTargetDataLine;
private AudioFormat mAudioFormat;
private String mName;
private int mBytesPerFrame = 0;
private SampleAdapter mSampleAdapter;
private Listener<ComplexBuffer> mListener;
/**
* Complex Mixer - constructs a reader on the mixer/sound card target
* data line using the specified audio format (sample size, sample rate )
* and broadcasts complex (I/Q) sample buffers to all registered listeners.
* Reads sample buffers sized to 10% of the sample rate specified in audio
* format.
*
* @param targetDataLine - mixer or sound card to be used
*
* @param format - audio format
*
* @param name - token name to use for this mixer
*
* @param sampleAdapter - adapter to convert byte array data read from the
* mixer into ComplexBuffer. The adapter can optionally invert the channel
* data if the left/right stereo channels are inverted.
*/
public ComplexMixer( TargetDataLine targetDataLine,
AudioFormat format,
String name,
SampleAdapter sampleAdapter,
Listener<ComplexBuffer> listener )
{
mTargetDataLine = targetDataLine;
mName = name;
mAudioFormat = format;
mSampleAdapter = sampleAdapter;
mListener = listener;
}
public TargetDataLine getTargetDataLine()
{
return mTargetDataLine;
}
public void start()
{
if( !mBufferReader.isRunning() )
{
Thread thread = new Thread( mBufferReader );
thread.setDaemon( true );
thread.setName( mName + " Complex Sample Reader" );
thread.start();
}
}
public void stop()
{
mBufferReader.stop();
}
private List<ComplexSample> convert( ComplexBuffer sampleBuffer )
{
float[] samples = sampleBuffer.getSamples();
ArrayList<ComplexSample> converted = new ArrayList<ComplexSample>();
for( int x = 0; x < samples.length; x += 2 )
{
converted.add( new ComplexSample( samples[ x ], samples[ x + 1 ] ) );
}
sampleBuffer.dispose();
return converted;
}
@Override
public String toString()
{
return mName;
}
public int getSampleRate()
{
if( mTargetDataLine != null )
{
return (int)mTargetDataLine.getFormat().getSampleRate();
}
else
{
return 0;
}
}
/**
* Returns the frequency of this source. Default is 0.
*/
public long getFrequency() throws SourceException
{
return mFrequency;
}
/**
* Specify the frequency that will be returned from this source. This may
* be useful if you are streaming an external audio source in through the
* sound card and you want to specify a frequency for that source
*/
public void setFrequency( long frequency )
{
mFrequency = frequency;
}
/**
* Reader thread. Performs blocking read against the mixer target data
* line, converts the samples to an array of floats using the specified
* adapter. Dispatches float arrays to all registered listeners.
*/
public class BufferReader implements Runnable
{
private AtomicBoolean mRunning = new AtomicBoolean();
@Override
public void run()
{
if( mRunning.compareAndSet( false, true ) )
{
if( mTargetDataLine == null )
{
mRunning.set( false );
mLog.error( "ComplexMixerSource - mixer target data line"
+ " is null" );
}
else
{
mBytesPerFrame = mAudioFormat.getSampleSizeInBits() / 8;
if( mBytesPerFrame == AudioSystem.NOT_SPECIFIED )
{
mBytesPerFrame = 2;
}
/* Set buffer size to 1/10 second of samples */
mBufferSize = (int)( mAudioFormat.getSampleRate()
* 0.05 ) * mBytesPerFrame;
/* We'll reuse the same buffer for each read */
byte[] buffer = new byte[ mBufferSize ];
try
{
mTargetDataLine.open( mAudioFormat );
mTargetDataLine.start();
}
catch ( LineUnavailableException e )
{
mLog.error( "ComplexMixerSource - mixer target data line"
+ "not available to read data from", e );
mRunning.set( false );
}
while( mRunning.get() )
{
try
{
/* Blocking read - waits until the buffer fills */
mTargetDataLine.read( buffer, 0, buffer.length );
/* Convert samples to float array */
float[] samples =
mSampleAdapter.convert( buffer );
/* Dispatch samples to registered listeners */
if( mListener != null )
{
mListener.receive( new ComplexBuffer( samples ) );
}
}
catch ( Exception e )
{
mLog.error( "error while reading"
+ "from the mixer target data line", e );
mRunning.set( false );
}
}
}
/* Close the data line if it is still open */
if( mTargetDataLine != null && mTargetDataLine.isOpen() )
{
mTargetDataLine.close();
}
}
}
/**
* Stops the reader thread
*/
public void stop()
{
if( mRunning.compareAndSet( true, false ) )
{
mTargetDataLine.stop();
mTargetDataLine.close();
}
}
/**
* Indicates if the reader thread is running
*/
public boolean isRunning()
{
return mRunning.get();
}
}
public void dispose()
{
if( mBufferReader != null )
{
mBufferReader.stop();
}
mListener = null;
}
}

View File

@@ -17,7 +17,7 @@
******************************************************************************/
package source.mixer;
import java.util.Set;
import java.util.List;
import javax.sound.sampled.Mixer;
import javax.swing.DefaultComboBoxModel;
@@ -27,12 +27,13 @@ import javax.swing.JLabel;
import source.SourceEditor;
import source.config.SourceConfigMixer;
import source.config.SourceConfiguration;
import source.mixer.MixerManager.DiscoveredMixer;
import controller.ResourceManager;
public class MixerEditor extends SourceEditor
{
private static final long serialVersionUID = 1L;
private JComboBox<String> mComboMixers;
private JComboBox<DiscoveredMixer> mComboMixers;
private JComboBox<MixerChannel>mComboChannels;
protected Mixer.Info mSelectedMixer = null;
@@ -50,7 +51,7 @@ public class MixerEditor extends SourceEditor
JLabel mixerLabel = new JLabel( "Mixer:" );
add( mixerLabel, "align right" );
mComboMixers = new JComboBox<String>();
mComboMixers = new JComboBox<DiscoveredMixer>();
add( mComboMixers, "wrap" );
@@ -76,11 +77,12 @@ public class MixerEditor extends SourceEditor
{
SourceConfigMixer config = (SourceConfigMixer)mConfig;
String mixer = mComboMixers.getItemAt( mComboMixers.getSelectedIndex() );
DiscoveredMixer selectedMixer =
mComboMixers.getItemAt( mComboMixers.getSelectedIndex() );
if( mixer != null )
if( selectedMixer != null )
{
config.setMixer( mixer );
config.setMixer( selectedMixer.getMixerName() );
}
MixerChannel channel = mComboChannels.getItemAt(
@@ -103,16 +105,16 @@ public class MixerEditor extends SourceEditor
@Override
public void run()
{
Set<String> mixers = MixerManager.getInstance().getMixers();
String[] mixerStrings = mixers.toArray( new String[ mixers.size() ] );
DiscoveredMixer[] mixers = MixerManager.getInstance().getMixers();
mComboMixers.setModel(
new DefaultComboBoxModel<String>( mixerStrings ) );
new DefaultComboBoxModel<DiscoveredMixer>( mixers ) );
SourceConfigMixer config = (SourceConfigMixer)mConfig;
String mixer = config.getMixer();
if( mixer != null && mixers.contains( mixer ) )
if( mixer != null )
{
mComboMixers.setSelectedItem( mixer );
}
@@ -121,11 +123,14 @@ public class MixerEditor extends SourceEditor
//Mixer either hasn't been set yet, or it was set to a
//mixer that is no longer available. Set the mixer to the
//first item, and then store the value
if( mixers.size() > 0 )
if( mixers.length > 0 )
{
mComboMixers.setSelectedIndex( 0 );
config.setMixer( mComboMixers.getItemAt( 0 ) );
DiscoveredMixer selectedMixer =
(DiscoveredMixer)mComboMixers.getSelectedItem();
config.setMixer( selectedMixer.getMixerName() );
}
}
}

View File

@@ -17,20 +17,25 @@
******************************************************************************/
package source.mixer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Set;
import java.util.List;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.TargetDataLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.adapter.ChannelShortAdapter;
import sample.adapter.ShortAdapter;
import source.config.SourceConfigMixer;
import source.config.SourceConfiguration;
import source.tuner.MixerTunerDataLine;
@@ -44,8 +49,8 @@ public class MixerManager
private static MixerManager sInstance = null;
private HashMap<String,TargetDataLine> mMixerLines =
new HashMap<String,TargetDataLine>();
private ArrayList<DiscoveredMixer> mDiscoveredMixers =
new ArrayList<DiscoveredMixer>();
private HashMap<String,MixerTunerDataLine> mMixerTuners =
new HashMap<String,MixerTunerDataLine>();
@@ -77,52 +82,109 @@ public class MixerManager
return sInstance;
}
public MixerSource getSource( ProcessingChain channel )
public SimplexMixerSource getSource( ProcessingChain processingChain )
{
MixerSource retVal = null;
SimplexMixerSource retVal = null;
SourceConfiguration config =
channel.getChannel().getSourceConfiguration();
processingChain.getChannel().getSourceConfiguration();
if( config instanceof SourceConfigMixer )
{
SourceConfigMixer mixerConfig = (SourceConfigMixer)config;
//Get the name of the requested mixer
String mixerName = mixerConfig.getMixer();
if( mixerName != null )
{
TargetDataLine tdl = mMixerLines.get( mixerName );
DiscoveredMixer mixer = getDiscoveredMixer( mixerName );
if( tdl != null )
if( mixer != null )
{
switch( mixerConfig.getChannel() )
MixerChannel channel = mixerConfig.getChannel();
if( mixer.supportsChannel( channel ) )
{
case MONO:
retVal = new MixerSource( mMixerLines.get( mixerName ),
MONO,
mixerName,
new ShortAdapter() );
break;
case LEFT:
case RIGHT:
retVal = new MixerSource( mMixerLines.get( mixerName ),
STEREO,
mixerName,
new ChannelShortAdapter( mixerConfig.getChannel() ) );
break;
if( channel == MixerChannel.MONO )
{
DataLine.Info info =
new DataLine.Info(TargetDataLine.class, MONO );
TargetDataLine dataLine;
try
{
dataLine = (TargetDataLine)mixer
.getMixer().getLine( info );
if( dataLine != null )
{
return new SimplexMixerSource( dataLine,
MONO,
mixerName,
new ShortAdapter() );
}
}
catch ( LineUnavailableException e )
{
mLog.error( "couldn't get mixer data line " +
"for [" + mixerName + "] for channel [" +
channel.name() + "]", e );
}
}
else
{
DataLine.Info info =
new DataLine.Info(TargetDataLine.class, STEREO );
TargetDataLine dataLine;
try
{
dataLine = (TargetDataLine)mixer
.getMixer().getLine( info );
if( dataLine != null )
{
return new SimplexMixerSource( dataLine, STEREO,
mixerName, new ChannelShortAdapter(
mixerConfig.getChannel() ) );
}
}
catch ( LineUnavailableException e )
{
mLog.error( "couldn't get mixer data line " +
"for [" + mixerName + "] for channel [" +
channel.name() + "]", e );
}
}
}
}
}
}
return retVal;
return null;
}
public Set<String> getMixers()
public DiscoveredMixer[] getMixers()
{
return mMixerLines.keySet();
return mDiscoveredMixers.toArray(
new DiscoveredMixer[ mDiscoveredMixers.size() ] );
}
public DiscoveredMixer getDiscoveredMixer( String name )
{
for( DiscoveredMixer mixer: mDiscoveredMixers )
{
if( mixer.getMixerName().contentEquals( name ) )
{
return mixer;
}
}
return null;
}
public Collection<MixerTunerDataLine> getMixerTunerDataLines()
@@ -147,22 +209,27 @@ public class MixerManager
if( mixer != null )
{
TargetDataLine tdl = getTargetDataLine( mixerInfo, STEREO );
if( tdl != null )
EnumSet<MixerChannel> channels = getSupportedChannels( mixer );
if( channels != null )
{
mMixerLines.put( mixerInfo.getName(), tdl );
sb.append( "\t[LOADED] Mixer:" + mixerInfo.getName() + "\n" );
mDiscoveredMixers.add(
new DiscoveredMixer( mixer, channels ) );
sb.append( "\t[LOADED] Mixer:" + mixerInfo.getName() +
" CHANNELS: " + channels + "\n" );
}
else
{
sb.append( "\t[NOT LOADED] Mixer:" + mixerInfo.getName() + " - audio format not supported\n" );
sb.append( "\t[NOT LOADED] Mixer:" +
mixerInfo.getName() +
" - audio format not supported\n" );
}
}
else
{
sb.append( "\t[NOT LOADED] Mixer:" + mixerInfo.getName() + " - couldn't get mixer\n" );
sb.append( "\t[NOT LOADED] Mixer:" + mixerInfo.getName() +
" - couldn't get mixer\n" );
}
}
else //Process as a Mixer-based tuner data line
@@ -226,6 +293,38 @@ public class MixerManager
return retVal;
}
private EnumSet<MixerChannel> getSupportedChannels( Mixer mixer )
{
DataLine.Info stereoInfo=
new DataLine.Info(TargetDataLine.class, STEREO );
boolean stereoSupported = mixer.isLineSupported( stereoInfo );
DataLine.Info monoInfo=
new DataLine.Info(TargetDataLine.class, MONO );
boolean monoSupported = mixer.isLineSupported( monoInfo );
if( stereoSupported && monoSupported )
{
return EnumSet.of( MixerChannel.LEFT,
MixerChannel.RIGHT,
MixerChannel.MONO );
}
else if( stereoSupported )
{
return EnumSet.of( MixerChannel.LEFT,
MixerChannel.RIGHT,
MixerChannel.MONO );
}
else if( monoSupported )
{
return EnumSet.of( MixerChannel.MONO );
}
return null;
}
public static String getMixerDevices()
{
StringBuilder sb = new StringBuilder();
@@ -264,4 +363,41 @@ public class MixerManager
return sb.toString();
}
public class DiscoveredMixer
{
private Mixer mMixer;
private EnumSet<MixerChannel> mChannels;
public DiscoveredMixer( Mixer mixer, EnumSet<MixerChannel> channels )
{
mMixer = mixer;
mChannels = channels;
}
public Mixer getMixer()
{
return mMixer;
}
public String getMixerName()
{
return mMixer.getMixerInfo().getName();
}
public EnumSet<MixerChannel> getChannels()
{
return mChannels;
}
public boolean supportsChannel( MixerChannel channel )
{
return mChannels.contains( channel );
}
public String toString()
{
return getMixerName();
}
}
}

View File

@@ -1,28 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.mixer;
/*
* Adapter to convert byte data received from a data stream into float[]
* samples
*/
public abstract class SampleAdapter
{
public abstract Float[] convert( byte[] samples );
}

View File

@@ -1,61 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.mixer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* Converts 16-bit little endian byte data into an array of floats
*/
public class ShortAdapter extends SampleAdapter
{
private ByteOrder mByteOrder = ByteOrder.LITTLE_ENDIAN;
@Override
public Float[] convert( byte[] samples )
{
Float[] processed = new Float[ samples.length / 2 ];
int pointer = 0;
/* Wrap byte array in a byte buffer so we can process them as shorts */
ByteBuffer buffer = ByteBuffer.wrap( samples );
/* Set endian to correct byte ordering */
buffer.order( mByteOrder );
while( buffer.hasRemaining() )
{
processed[ pointer ] = (float)buffer.getShort();
pointer++;
}
return processed;
}
/**
* Set byte interpretation to little or big endian. Defaults to LITTLE
* endian
*/
public void setByteOrder( ByteOrder order )
{
mByteOrder = order;
}
}

View File

@@ -17,7 +17,6 @@
******************************************************************************/
package source.mixer;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -31,13 +30,15 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import source.FloatArraySource;
import sample.adapter.SampleAdapter;
import sample.simplex.SimplexBuffer;
import source.SimplexSource;
import source.SourceException;
public class MixerSource extends FloatArraySource
public class SimplexMixerSource extends SimplexSource
{
private final static Logger mLog =
LoggerFactory.getLogger( MixerSource.class );
LoggerFactory.getLogger( SimplexMixerSource.class );
private long mFrequency = 0;
private int mBufferSize = 16384;
@@ -49,14 +50,14 @@ public class MixerSource extends FloatArraySource
private int mBytesPerFrame = 0;
private SampleAdapter mSampleAdapter;
CopyOnWriteArrayList<Listener<Float[]>> mSampleListeners =
new CopyOnWriteArrayList<Listener<Float[]>>();
CopyOnWriteArrayList<Listener<SimplexBuffer>> mSampleListeners =
new CopyOnWriteArrayList<Listener<SimplexBuffer>>();
/**
* Complex Mixer Source - constructs a reader on the mixer/sound card target
* Simplex Mixer Source - constructs a reader on the mixer/sound card target
* data line using the specified audio format (sample size, sample rate )
* and broadcasts float[] sample data to all registered listeners. Reads
* sample buffers sized to 10% of the sample rate specified in audio format.
* and broadcasts simplex buffers (float arrays) to all registered listeners.
* Reads buffers sized to 10% of the sample rate specified in audio format.
*
* @param targetDataLine - mixer or sound card to be used
*
@@ -68,7 +69,7 @@ public class MixerSource extends FloatArraySource
* mixer into float array data. Can optionally invert the channel data if
* the left/right stereo channels are inverted.
*/
public MixerSource( TargetDataLine targetDataLine,
public SimplexMixerSource( TargetDataLine targetDataLine,
AudioFormat format,
String name,
SampleAdapter sampleAdapter )
@@ -80,7 +81,7 @@ public class MixerSource extends FloatArraySource
mSampleAdapter = sampleAdapter;
}
public void setListener( Listener<Float[]> listener )
public void setListener( Listener<SimplexBuffer> listener )
{
mSampleListeners.add( listener );
@@ -88,12 +89,13 @@ public class MixerSource extends FloatArraySource
if( !mBufferReader.isRunning() )
{
Thread thread = new Thread( mBufferReader );
thread.setDaemon( true );
thread.setName( getName() + " Sample Reader" );
thread.start();
}
}
public void removeListener( Listener<Float[]> listener )
public void removeListener( Listener<SimplexBuffer> listener )
{
mSampleListeners.remove( listener );
@@ -104,19 +106,19 @@ public class MixerSource extends FloatArraySource
}
}
public void broadcast( Float[] samples )
public void broadcast( SimplexBuffer samples )
{
Iterator<Listener<Float[]>> it = mSampleListeners.iterator();
Iterator<Listener<SimplexBuffer>> it = mSampleListeners.iterator();
while( it.hasNext() )
{
Listener<Float[]> next = it.next();
Listener<SimplexBuffer> 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( Arrays.copyOf( samples, samples.length ) );
next.receive( samples.copyOf() );
}
else
{
@@ -220,11 +222,11 @@ public class MixerSource extends FloatArraySource
mTargetDataLine.read( buffer, 0, buffer.length );
/* Convert samples to float array */
Float[] convertedSamples =
float[] samples =
mSampleAdapter.convert( buffer );
/* Dispatch samples to registered listeners */
broadcast( convertedSamples );
broadcast( new SimplexBuffer( samples ) );
}
catch ( Exception e )
{

View File

@@ -25,25 +25,25 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.adapter.SampleAdapter;
import sample.complex.ComplexBuffer;
import sample.complex.ComplexSampleListListener;
import source.mixer.MixerSource;
import source.mixer.SampleAdapter;
import source.mixer.ComplexMixer;
public abstract class MixerTuner extends Tuner
public abstract class MixerTuner extends Tuner implements Listener<ComplexBuffer>
{
private final static Logger mLog =
LoggerFactory.getLogger( MixerTuner.class );
private TargetDataLine mTargetDataLine;
protected MixerTunerType mMixerTunerType;
protected MixerSource mMixerSource;
protected ComplexMixer mComplexMixer;
protected ArrayList<ComplexSampleListListener> mListeners =
new ArrayList<ComplexSampleListListener>();
/**
* Provides a wrapper around a MixerSource to treat it like a Tuner that
* can provide ComplexSample<Short> samples. Subclasses should couple this
* functionality with a tuner control class to allow tuning.
* Wrapper class to add basic Tuner functionality to the ComplexMixer.
* Subclasses should couple this functionality with a tuner controller class
* to support tuning.
*/
public MixerTuner( String name,
MixerTunerType mixerTunerType,
@@ -54,12 +54,11 @@ public abstract class MixerTuner extends Tuner
mMixerTunerType = mixerTunerType;
mTargetDataLine = targetDataLine;
mMixerSource = new MixerSource( targetDataLine,
mMixerTunerType.getAudioFormat(),
name,
sampleAdapter );
mComplexMixer = new ComplexMixer( targetDataLine,
mMixerTunerType.getAudioFormat(),
name,
sampleAdapter,
(Listener<ComplexBuffer>)this );
}
public MixerTuner( String name,
@@ -72,6 +71,13 @@ public abstract class MixerTuner extends Tuner
sampleAdapter );
}
public void dispose()
{
mComplexMixer.stop();
super.dispose();
}
public MixerTunerType getMixerTunerType()
{
return mMixerTunerType;
@@ -79,26 +85,44 @@ public abstract class MixerTuner extends Tuner
public TargetDataLine getTargetDataLine()
{
return mTargetDataLine;
return mComplexMixer.getTargetDataLine();
}
/**
* Overrides the Tuner listener management methods and allows the
* complex mixer source to manage sample listeners
* Dispatches the received complex buffers from the mComplexMixer to all
* listeners registered on the parent Tuner class
*/
@Override
public void addListener( Listener<Float[]> listener )
public void receive( ComplexBuffer buffer )
{
super.broadcast( buffer );
}
/**
* Overrides the parent Tuner class listener management method to allow
* starting the buffer processing thread
*/
@Override
public void addListener( Listener<ComplexBuffer> listener )
{
mMixerSource.setListener( listener );
super.addListener( listener );
/* We can call start multiple times -- it ignores additional requests */
mComplexMixer.start();
}
/**
* Overrides the Tuner listener management methods and allows the
* complex mixer source to manage sample listeners
* Overrides the parent Tuner class listener management method to allow
* stopping the buffer processing thread when there are no more listeners
*/
@Override
public void removeListener( Listener<Float[]> listener )
public void removeListener( Listener<ComplexBuffer> listener )
{
mMixerSource.removeListener( listener );
super.removeListener( listener );
if( mSampleListeners.isEmpty() )
{
mComplexMixer.stop();
}
}
}

View File

@@ -23,6 +23,7 @@ import java.util.concurrent.RejectedExecutionException;
import javax.swing.JPanel;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.FrequencyChangeEvent.Attribute;
import controller.ResourceManager;
@@ -37,8 +38,12 @@ public abstract class Tuner implements FrequencyChangeBroadcaster,
{
private String mName;
protected CopyOnWriteArrayList<Listener<Float[]>>
mSampleListeners = new CopyOnWriteArrayList<Listener<Float[]>>();
/**
* Sample Listeners - these will typically be the DFT processor for spectral
* display, or it will be one or more tuner channel sources
*/
protected CopyOnWriteArrayList<Listener<ComplexBuffer>>
mSampleListeners = new CopyOnWriteArrayList<Listener<ComplexBuffer>>();
protected CopyOnWriteArrayList<FrequencyChangeListener>
mFrequencyChangeListeners = new CopyOnWriteArrayList<FrequencyChangeListener>();
@@ -53,7 +58,11 @@ public abstract class Tuner implements FrequencyChangeBroadcaster,
return mName;
}
public abstract void dispose();
public void dispose()
{
mSampleListeners.clear();
mFrequencyChangeListeners.clear();
}
public void setName( String name )
{
@@ -141,7 +150,7 @@ public abstract class Tuner implements FrequencyChangeBroadcaster,
/**
* Registers the listener to receive complex float sample arrays
*/
public void addListener( Listener<Float[]> listener )
public void addListener( Listener<ComplexBuffer> listener )
{
mSampleListeners.add( listener );
}
@@ -149,7 +158,7 @@ public abstract class Tuner implements FrequencyChangeBroadcaster,
/**
* Removes the registered listener
*/
public void removeListener( Listener<Float[]> listener )
public void removeListener( Listener<ComplexBuffer> listener )
{
mSampleListeners.remove( listener );
}
@@ -157,11 +166,11 @@ public abstract class Tuner implements FrequencyChangeBroadcaster,
/**
* Broadcasts the samples to all registered listeners
*/
public void broadcast( Float[] samples )
public void broadcast( ComplexBuffer sampleBuffer )
{
for( Listener<Float[]> listener: mSampleListeners )
for( Listener<ComplexBuffer> listener: mSampleListeners )
{
listener.receive( samples );
listener.receive( sampleBuffer );
}
}
@@ -228,15 +237,4 @@ public abstract class Tuner implements FrequencyChangeBroadcaster,
{
broadcastFrequencyChangeEvent( event );
}
// /**
// * Broadcasts a frequency change event to all registered listeners
// */
// public void broadcastBandwidthChange( int bandwidth )
// {
// broadcastFrequencyChangeEvent(
// new FrequencyChangeEvent( Attribute.BANDWIDTH, bandwidth ) );
// }
//
}

View File

@@ -25,6 +25,7 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import sample.Listener;
import sample.complex.ComplexBuffer;
import sample.complex.ComplexSample;
import sample.complex.ComplexSampleAssembler;
import source.ComplexSource;
@@ -39,13 +40,13 @@ import dsp.filter.Window.WindowType;
public class TunerChannelSource extends ComplexSource
implements FrequencyChangeListener,
Listener<Float[]>
Listener<ComplexBuffer>
{
private static int sCHANNEL_RATE = 48000;
private static int sCHANNEL_PASS_FREQUENCY = 12500;
private LinkedTransferQueue<Float[]> mBuffer =
new LinkedTransferQueue<Float[]>();
private LinkedTransferQueue<ComplexBuffer> mBuffer =
new LinkedTransferQueue<ComplexBuffer>();
private Tuner mTuner;
private TunerChannel mTunerChannel;
private Oscillator mSineWaveGenerator;
@@ -91,7 +92,7 @@ public class TunerChannelSource extends ComplexSource
new DecimationProcessor(), 50, TimeUnit.MILLISECONDS );
/* Finally, register to receive samples from the tuner */
mTuner.addListener( (Listener<Float[]>)this );
mTuner.addListener( (Listener<ComplexBuffer>)this );
}
@Override
@@ -137,9 +138,9 @@ public class TunerChannelSource extends ComplexSource
}
@Override
public void receive( Float[] samples )
public void receive( ComplexBuffer sampleArray )
{
mBuffer.add( samples );
mBuffer.add( sampleArray );
}
@Override
@@ -242,17 +243,20 @@ public class TunerChannelSource extends ComplexSource
@Override
public void run()
{
List<Float[]> samplesList = new ArrayList<Float[]>();
List<ComplexBuffer> sampleBuffers =
new ArrayList<ComplexBuffer>();
if( mBuffer != null )
{
/* Limit to 10, 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( samplesList, 10 );
mBuffer.drainTo( sampleBuffers, 10 );
for( Float[] samples: samplesList )
for( ComplexBuffer sampleArray: sampleBuffers )
{
float[] samples = sampleArray.getSamples();
for( int x = 0; x < samples.length; x += 2 )
{
Float left = samples[ x ];
@@ -273,7 +277,7 @@ public class TunerChannelSource extends ComplexSource
}
}
samplesList.clear();
sampleBuffers.clear();
}
}
}

View File

@@ -135,6 +135,10 @@ public abstract class TunerController implements Tunable
long maxLockedFrequency = mTunedChannels
.get( mTunedChannels.size() - 1 ).getMaxFrequency();
mLog.debug( "Tuner Controller min [" + minLockedFrequency +
"] max [" + maxLockedFrequency +
"] bw [" + getBandwidth() + "]" );
//Requested channel is higher than min locked frequency
if( minLockedFrequency <= channel.getMinFrequency() &&
( channel.getMaxFrequency() - minLockedFrequency ) <= getBandwidth() )

View File

@@ -364,7 +364,14 @@ public class TunerManager
HackRFTuner tuner = new HackRFTuner( hackRFController );
return new TunerInitStatus( tuner, "LOADED" );
TunerConfiguration config = getTunerConfiguration( tuner );
if( config != null )
{
tuner.apply( config );
}
return new TunerInitStatus( tuner, "LOADED" );
}
catch( SourceException se )
{
@@ -475,98 +482,6 @@ public class TunerManager
return new TunerInitStatus( null, reason );
}
// /**
// * Get all USB tuner devices
// */
// private ArrayList<USBTunerDevice> getUSBTunerDevices()
// {
// StringBuilder sb = new StringBuilder();
//
// List<UsbDevice> devices = getUSBDevices();
//
// sb.append( "discovered [" + devices.size() +
// "] attached USB devices\n" );
//
// ArrayList<USBTunerDevice> tuners = new ArrayList<USBTunerDevice>();
//
// for (UsbDevice device : devices )
// {
// String status = "\t[NOT RECOGNIZED]\t";
//
// TunerClass type = TunerClass.valueOf( device.getUsbDeviceDescriptor() );
//
// if( type != TunerClass.UNKNOWN )
// {
// status = "\t[RECOGNIZED] \t";
// tuners.add( new USBTunerDevice( device, type) );
// }
//
// sb.append( status );
// sb.append( device.toString() );
//
// if( type != TunerClass.UNKNOWN )
// {
// sb.append( " " );
// sb.append( type.getVendorDescription() );
// sb.append( " " );
// sb.append( type.getDeviceDescription() );
// }
//
// sb.append( "\n" );
// }
//
// mLog.info( "USB Device Discovery" );
// mLog.info( sb.toString() );
//
// return tuners;
// }
//
// /**
// * Gets all USB devices from the root hub downward, recursively, or
// * returns empty list if none are discovered
// */
// public static List<UsbDevice> getUSBDevices()
// {
// ArrayList<UsbDevice> devices = new ArrayList<UsbDevice>();
//
// try
// {
// UsbServices services = UsbHostManager.getUsbServices();
// devices = getUsbChildDevices( services.getRootUsbHub() );
// }
// catch( SecurityException e )
// {
// mLog.error( "security exception while getting USB devices", e );
// }
// catch( UsbException e )
// {
// mLog.error( "usb exception while getting USB devices", e );
// }
//
// return devices;
// }
//
// /**
// * Get (recursively) all usb child devices below the usb hub
// */
// public static ArrayList<UsbDevice> getUsbChildDevices( UsbHub hub )
// {
// ArrayList<UsbDevice> devices = new ArrayList<UsbDevice>();
//
// for ( UsbDevice device : (List<UsbDevice>) hub.getAttachedUsbDevices() )
// {
// devices.add( device );
//
// if ( device.isUsbHub() )
// {
// devices.addAll( getUsbChildDevices( (UsbHub)device ) );
// }
// }
//
// return devices;
// }
/**
* Get a stored tuner configuration or create a default tuner configuration
* for the tuner type, connected at the address.

View File

@@ -22,8 +22,9 @@ import java.util.concurrent.RejectedExecutionException;
import javax.swing.JPanel;
import sample.Listener;
import sample.adapter.ShortAdapter;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.mixer.ShortAdapter;
import source.tuner.FrequencyChangeListener;
import source.tuner.MixerTuner;
import source.tuner.MixerTunerDataLine;
@@ -122,7 +123,7 @@ public class FCDTuner extends MixerTuner
if( source != null )
{
/* Unregister for receiving samples */
removeListener( (Listener<Float[]>)source );
removeListener( (Listener<ComplexBuffer>)source );
/* Tell the controller to release the channel and cleanup */
/* This will release the channel as a frequency change listener */

View File

@@ -26,6 +26,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.Tuner;
import source.tuner.TunerChannel;
@@ -102,7 +103,7 @@ public class HackRFTuner extends Tuner
@Override
public void apply( TunerConfiguration config ) throws SourceException
{
mLog.error( "B100 Tuner - can't apply tuner configuration" );
mController.apply( config );
}
@Override
@@ -128,7 +129,7 @@ public class HackRFTuner extends Tuner
public void releaseChannel( TunerChannelSource source )
{
/* Unregister for receiving samples */
removeListener( (Listener<Float[]>)source );
removeListener( (Listener<ComplexBuffer>)source );
/* Tell the controller to release the channel and cleanup */
if( source != null )
@@ -153,13 +154,13 @@ public class HackRFTuner extends Tuner
}
@Override
public void addListener( Listener<Float[]> listener )
public void addListener( Listener<ComplexBuffer> listener )
{
mController.addListener( listener );
}
@Override
public void removeListener( Listener<Float[]> listener )
public void removeListener( Listener<ComplexBuffer> listener )
{
mController.removeListener( listener );
}

View File

@@ -53,7 +53,6 @@ package source.tuner.hackrf;
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
@@ -80,6 +79,7 @@ import org.usb4java.Transfer;
import org.usb4java.TransferCallback;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.TunerConfiguration;
import source.tuner.TunerController;
@@ -114,8 +114,8 @@ public class HackRFTunerController extends TunerController
private LinkedTransferQueue<byte[]> mFilledBuffers =
new LinkedTransferQueue<byte[]>();
private CopyOnWriteArrayList<Listener<Float[]>> mSampleListeners =
new CopyOnWriteArrayList<Listener<Float[]>>();
private CopyOnWriteArrayList<Listener<ComplexBuffer>> mSampleListeners =
new CopyOnWriteArrayList<Listener<ComplexBuffer>>();
private ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter();
private BufferProcessor mBufferProcessor = new BufferProcessor();
@@ -491,6 +491,8 @@ public class HackRFTunerController extends TunerController
*/
public void setSampleRate( HackRFSampleRate rate ) throws UsbException
{
mLog.debug( "Setting sample rate to:" + rate.name() );
setSampleRateManual( rate.getRate(), 1 );
mFrequencyController.setSampleRate( rate.getRate() );
@@ -561,11 +563,11 @@ public class HackRFTunerController extends TunerController
RATE2_016MHZ( 2016000, "2.016 MHz", BasebandFilter.F3_50 ),
RATE3_024MHZ( 3024000, "3.024 MHz", BasebandFilter.F5_00 ),
RATE4_464MHZ( 4464000, "4.464 MHz", BasebandFilter.F6_00 ),
RATE5_472MHZ( 5472000, "5.472 MHz", BasebandFilter.F7_00 ),
RATE5_376MHZ( 5376000, "5.376 MHz", BasebandFilter.F7_00 ),
RATE7_488MHZ( 7488000, "7.488 MHz", BasebandFilter.F9_00 ),
RATE10_032MHZ( 9984000, " 9.984 MHz", BasebandFilter.F12_00 ),
RATE10_080MHZ( 10080000, "10.080 MHz", BasebandFilter.F12_00 ),
RATE12_000MHZ( 12000000, "12.000 MHz", BasebandFilter.F14_00 ),
RATE13_488MHZ( 13488000, "13.488 MHz", BasebandFilter.F15_00 ),
RATE13_440MHZ( 13440000, "13.440 MHz", BasebandFilter.F15_00 ),
RATE14_976MHZ( 14976000, "14.976 MHz", BasebandFilter.F20_00 ),
RATE19_968MHZ( 19968000, "19.968 MHz", BasebandFilter.F24_00 );
@@ -868,7 +870,7 @@ public class HackRFTunerController extends TunerController
* Adds a sample listener. If the buffer processing thread is
* not currently running, starts it running in a new thread.
*/
public void addListener( Listener<Float[]> listener )
public void addListener( Listener<ComplexBuffer> listener )
{
mSampleListeners.add( listener );
@@ -878,7 +880,7 @@ public class HackRFTunerController extends TunerController
Thread thread = new Thread( mBufferProcessor );
thread.setDaemon( true );
thread.setName( "Sample Processor" );
thread.setName( "HackRF Sample Processor" );
thread.start();
}
@@ -888,7 +890,7 @@ public class HackRFTunerController extends TunerController
* Removes the sample listener. If this is the last registered listener,
* shuts down the buffer processing thread.
*/
public void removeListener( Listener<Float[]> listener )
public void removeListener( Listener<ComplexBuffer> listener )
{
mSampleListeners.remove( listener );
@@ -906,19 +908,19 @@ public class HackRFTunerController extends TunerController
* This is to facilitate garbage collection of the array when the listener
* is done processing the samples.
*/
public void broadcast( Float[] samples )
public void broadcast( ComplexBuffer samples )
{
Iterator<Listener<Float[]>> it = mSampleListeners.iterator();
Iterator<Listener<ComplexBuffer>> it = mSampleListeners.iterator();
while( it.hasNext() )
{
Listener<Float[]> next = it.next();
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( Arrays.copyOf( samples, samples.length ) );
next.receive( samples.copyOf() );
}
else
{
@@ -936,7 +938,7 @@ public class HackRFTunerController extends TunerController
private ScheduledExecutorService mExecutor =
Executors.newScheduledThreadPool( 1 );
private ScheduledFuture<?> mSampleDispatcherTask;
private ArrayList<Transfer> mTransfers;
private CopyOnWriteArrayList<Transfer> mTransfers;
private AtomicBoolean mRunning = new AtomicBoolean();
@Override
@@ -1078,7 +1080,7 @@ public class HackRFTunerController extends TunerController
*/
private void prepareTransfers() throws LibUsbException
{
mTransfers = new ArrayList<Transfer>();
mTransfers = new CopyOnWriteArrayList<Transfer>();
for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ )
{
@@ -1121,10 +1123,9 @@ public class HackRFTunerController extends TunerController
for( byte[] buffer: buffers )
{
//TODO: convert this to float primitive
Float[] samples = mSampleAdapter.convert( buffer );
float[] samples = mSampleAdapter.convert( buffer );
broadcast( samples );
broadcast( new ComplexBuffer( samples ) );
}
}
}

View File

@@ -17,7 +17,8 @@
******************************************************************************/
package source.tuner.rtl;
import source.mixer.SampleAdapter;
import sample.adapter.SampleAdapter;
import sample.complex.ComplexBuffer;
public class ByteSampleAdapter extends SampleAdapter
{
@@ -40,9 +41,9 @@ public class ByteSampleAdapter extends SampleAdapter
}
@Override
public Float[] convert( byte[] samples )
public float[] convert( byte[] samples )
{
Float[] convertedSamples = new Float[ samples.length ];
float[] convertedSamples = new float[ samples.length ];
int pointer = 0;
for( byte sample: samples )

View File

@@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory;
import org.usb4java.LibUsbException;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.FrequencyChangeListener;
import source.tuner.Tuner;
@@ -165,7 +166,7 @@ public class RTL2832Tuner extends Tuner
public void releaseChannel( TunerChannelSource source )
{
/* Unregister for receiving samples */
removeListener( (Listener<Float[]>)source );
removeListener( (Listener<ComplexBuffer>)source );
/* Tell the controller to release the channel and cleanup */
if( source != null )
@@ -175,13 +176,13 @@ public class RTL2832Tuner extends Tuner
}
@Override
public void addListener( Listener<Float[]> listener )
public void addListener( Listener<ComplexBuffer> listener )
{
mController.addListener( listener );
}
@Override
public void removeListener( Listener<Float[]> listener )
public void removeListener( Listener<ComplexBuffer> listener )
{
mController.removeListener( listener );
}

View File

@@ -52,12 +52,12 @@ import org.usb4java.Transfer;
import org.usb4java.TransferCallback;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.FrequencyChangeEvent;
import source.tuner.FrequencyChangeEvent.Attribute;
import source.tuner.TunerController;
import source.tuner.TunerType;
import util.TimeStamp;
import buffer.FloatAveragingBuffer;
import controller.ResourceManager;
@@ -107,8 +107,8 @@ public abstract class RTL2832TunerController extends TunerController
private ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter();
private CopyOnWriteArrayList<Listener<Float[]>> mSampleListeners =
new CopyOnWriteArrayList<Listener<Float[]>>();
private CopyOnWriteArrayList<Listener<ComplexBuffer>> mSampleListeners =
new CopyOnWriteArrayList<Listener<ComplexBuffer>>();
private LinkedTransferQueue<byte[]> mFilledBuffers =
new LinkedTransferQueue<byte[]>();
@@ -1342,7 +1342,7 @@ public abstract class RTL2832TunerController extends TunerController
* Adds a sample listener. If the buffer processing thread is
* not currently running, starts it running in a new thread.
*/
public void addListener( Listener<Float[]> listener )
public void addListener( Listener<ComplexBuffer> listener )
{
mSampleListeners.add( listener );
@@ -1352,7 +1352,7 @@ public abstract class RTL2832TunerController extends TunerController
Thread thread = new Thread( mBufferProcessor );
thread.setDaemon( true );
thread.setName( "Sample Processor" );
thread.setName( "RTL2832 Sample Processor" );
thread.start();
}
@@ -1362,7 +1362,7 @@ public abstract class RTL2832TunerController extends TunerController
* Removes the sample listener. If this is the last registered listener,
* shuts down the buffer processing thread.
*/
public void removeListener( Listener<Float[]> listener )
public void removeListener( Listener<ComplexBuffer> listener )
{
mSampleListeners.remove( listener );
@@ -1375,23 +1375,23 @@ public abstract class RTL2832TunerController extends TunerController
/**
* Dispatches float sample buffers to all registered listeners
*/
public void broadcast( Float[] samples )
public void broadcast( ComplexBuffer buffer )
{
Iterator<Listener<Float[]>> it = mSampleListeners.iterator();
Iterator<Listener<ComplexBuffer>> it = mSampleListeners.iterator();
while( it.hasNext() )
{
Listener<Float[]> next = it.next();
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( Arrays.copyOf( samples, samples.length ) );
next.receive( buffer.copyOf() );
}
else
{
next.receive( samples );
next.receive( buffer );
}
}
}
@@ -1406,7 +1406,7 @@ public abstract class RTL2832TunerController extends TunerController
Executors.newScheduledThreadPool( 2 );
private ScheduledFuture<?> mSampleDispatcherTask;
private ScheduledFuture<?> mSampleRateCounterTask;
private ArrayList<Transfer> mTransfers;
private CopyOnWriteArrayList<Transfer> mTransfers;
private AtomicBoolean mRunning = new AtomicBoolean();
@Override
@@ -1573,7 +1573,7 @@ public abstract class RTL2832TunerController extends TunerController
*/
private void prepareTransfers() throws LibUsbException
{
mTransfers = new ArrayList<Transfer>();
mTransfers = new CopyOnWriteArrayList<Transfer>();
for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ )
{
@@ -1616,10 +1616,9 @@ public abstract class RTL2832TunerController extends TunerController
for( byte[] buffer: buffers )
{
//TODO: convert this to float primitive
Float[] samples = mSampleAdapter.convert( buffer );
float[] samples = mSampleAdapter.convert( buffer );
broadcast( samples );
broadcast( new ComplexBuffer( samples ) );
}
}
}

View File

@@ -29,7 +29,7 @@ import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
public class FloatWaveSource extends WaveSource
{
@@ -44,7 +44,7 @@ public class FloatWaveSource extends WaveSource
private int mBufferPointer = 0;
private int mBufferLength;
private ShortBuffer mBufferWrapper;
private Listener<Float> mListener;
private SimplexSampleListener mListener;
private static int BUFFER_SAMPLE_SIZE = 2000;
@@ -272,12 +272,12 @@ public class FloatWaveSource extends WaveSource
/**
* Adds a new listener to receive samples as they are read from the wave file
*/
public void setListener( Listener<Float> listener )
public void setListener( SimplexSampleListener listener )
{
mListener = listener;
}
public void removeListener( Listener<Float> listener )
public void removeListener( SimplexSampleListener listener )
{
mListener = null;
}