[WIP] Implements reusable sample buffers across all components. This

produces a significant reduction in memory usage, especially for tuners
like the airspy and hackrf that produce 10/20 MSPS sample rates.  This
is observed to be around 66% reduction in peak memory usage and provides
a more stable memory utilization across time reducing stress on the
garbage collector.

Use of these reusable memory buffers will be pushed further down into
the processing chain modules to ensure we keep new object creation
to the lowest levels possible.
This commit is contained in:
Denny
2018-02-03 10:05:16 -05:00
parent fcd3c2fd46
commit f2f2c92afe
56 changed files with 1630 additions and 1255 deletions

View File

@@ -32,12 +32,11 @@ public abstract class Module
* ICallEventProvider provides call events
* IChannelEventListener consumes channel events
* IChannelEventProvider provides channel events (reset, selection, start, etc)
* IComplexBufferListener consumes complex samples and normally
* produces demodulated real sample buffers
* IFrequencyCorrectionController provides tuned frequency error corrections
* IMessageProvider produces messages
* IRealBufferListener consumes demodulated real sample buffers
* IRealBufferListener produces demodulated real sample buffers
* IReusableComplexBufferListener consumes complex samples and normally produces demodulated real sample buffers
* ISquelchStateListener consumes squelch states
* ISquelchStateProvider provides squelch states
*/

View File

@@ -49,8 +49,8 @@ import io.github.dsheirer.record.wave.ComplexBufferWaveRecorder;
import io.github.dsheirer.record.wave.RealBufferWaveRecorder;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.complex.ComplexBuffer;
import io.github.dsheirer.sample.complex.IComplexBufferListener;
import io.github.dsheirer.sample.complex.reusable.IReusableComplexBufferListener;
import io.github.dsheirer.sample.complex.reusable.ReusableBufferBroadcaster;
import io.github.dsheirer.sample.real.IFilteredRealBufferListener;
import io.github.dsheirer.sample.real.IFilteredRealBufferProvider;
import io.github.dsheirer.sample.real.IUnFilteredRealBufferListener;
@@ -98,7 +98,7 @@ public class ProcessingChain implements IChannelEventListener
private Broadcaster<AudioPacket> mAudioPacketBroadcaster = new Broadcaster<>();
private Broadcaster<CallEvent> mCallEventBroadcaster = new Broadcaster<>();
private Broadcaster<ChannelEvent> mChannelEventBroadcaster = new Broadcaster<>();
private Broadcaster<ComplexBuffer> mComplexBufferBroadcaster = new Broadcaster<>();
private ReusableBufferBroadcaster mComplexBufferBroadcaster = new ReusableBufferBroadcaster();
private Broadcaster<DecoderStateEvent> mDecoderStateEventBroadcaster = new Broadcaster<>();
private Broadcaster<Heartbeat> mHeartbeatBroadcaster = new Broadcaster<>();
private Broadcaster<SourceEvent> mSourceEventBroadcaster = new Broadcaster<>();
@@ -307,24 +307,14 @@ public class ProcessingChain implements IChannelEventListener
mChannelEventBroadcaster.addListener(((IChannelEventListener) module).getChannelEventListener());
}
if(module instanceof IComplexBufferListener)
{
mComplexBufferBroadcaster.addListener(((IComplexBufferListener) module).getComplexBufferListener());
}
if(module instanceof IDecoderStateEventListener)
{
mDecoderStateEventBroadcaster.addListener(((IDecoderStateEventListener) module).getDecoderStateListener());
}
if(module instanceof ISourceEventListener)
if(module instanceof IFilteredRealBufferListener)
{
Listener<SourceEvent> listener = ((ISourceEventListener)module).getSourceEventListener();
if(listener != null)
{
mSourceEventBroadcaster.addListener(listener);
}
mFilteredRealBufferBroadcaster.addListener(((IFilteredRealBufferListener) module).getFilteredRealBufferListener());
}
if(module instanceof IHeartbeatListener)
@@ -337,9 +327,19 @@ public class ProcessingChain implements IChannelEventListener
mMessageBroadcaster.addListener(((IMessageListener) module).getMessageListener());
}
if(module instanceof IFilteredRealBufferListener)
if(module instanceof IReusableComplexBufferListener)
{
mFilteredRealBufferBroadcaster.addListener(((IFilteredRealBufferListener) module).getFilteredRealBufferListener());
mComplexBufferBroadcaster.addListener(((IReusableComplexBufferListener) module).getReusableComplexBufferListener());
}
if(module instanceof ISourceEventListener)
{
Listener<SourceEvent> listener = ((ISourceEventListener)module).getSourceEventListener();
if(listener != null)
{
mSourceEventBroadcaster.addListener(listener);
}
}
if(module instanceof ISquelchStateListener)
@@ -379,19 +379,14 @@ public class ProcessingChain implements IChannelEventListener
mChannelEventBroadcaster.removeListener(((IChannelEventListener) module).getChannelEventListener());
}
if(module instanceof IComplexBufferListener)
{
mComplexBufferBroadcaster.removeListener(((IComplexBufferListener) module).getComplexBufferListener());
}
if(module instanceof IDecoderStateEventListener)
{
mDecoderStateEventBroadcaster.removeListener(((IDecoderStateEventListener) module).getDecoderStateListener());
}
if(module instanceof ISourceEventListener)
if(module instanceof IFilteredRealBufferListener)
{
mSourceEventBroadcaster.removeListener(((ISourceEventListener) module).getSourceEventListener());
mFilteredRealBufferBroadcaster.removeListener(((IFilteredRealBufferListener) module).getFilteredRealBufferListener());
}
if(module instanceof IHeartbeatListener)
@@ -399,16 +394,21 @@ public class ProcessingChain implements IChannelEventListener
mHeartbeatBroadcaster.removeListener(((IHeartbeatListener)module).getHeartbeatListener());
}
if(module instanceof IReusableComplexBufferListener)
{
mComplexBufferBroadcaster.removeListener(((IReusableComplexBufferListener) module).getReusableComplexBufferListener());
}
if(module instanceof ISourceEventListener)
{
mSourceEventBroadcaster.removeListener(((ISourceEventListener) module).getSourceEventListener());
}
if(module instanceof IMessageListener)
{
mMessageBroadcaster.removeListener(((IMessageListener) module).getMessageListener());
}
if(module instanceof IFilteredRealBufferListener)
{
mFilteredRealBufferBroadcaster.removeListener(((IFilteredRealBufferListener) module).getFilteredRealBufferListener());
}
if(module instanceof ISquelchStateListener)
{
mSquelchStateBroadcaster.removeListener(((ISquelchStateListener) module).getSquelchStateListener());

View File

@@ -1,19 +1,17 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* sdr-trunk
* Copyright (C) 2014-2018 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 io.github.dsheirer.module.decode.p25;
@@ -33,7 +31,8 @@ import io.github.dsheirer.instrument.tap.stream.QPSKTap;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.complex.ComplexBuffer;
import io.github.dsheirer.sample.complex.ComplexBufferToStreamConverter;
import io.github.dsheirer.sample.complex.IComplexBufferListener;
import io.github.dsheirer.sample.complex.reusable.IReusableComplexBufferListener;
import io.github.dsheirer.sample.complex.reusable.ReusableComplexBuffer;
import io.github.dsheirer.source.SourceEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,192 +40,207 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener
public class P25_LSMDecoder extends P25Decoder implements IReusableComplexBufferListener
{
private final static Logger mLog = LoggerFactory.getLogger( P25_LSMDecoder.class );
private final static Logger mLog = LoggerFactory.getLogger(P25_LSMDecoder.class);
/* Instrumentation Taps */
private static final String INSTRUMENT_BASEBAND_FILTER_OUTPUT = "Tap Point: Baseband Filter Output";
private static final String INSTRUMENT_AGC_OUTPUT = "Tap Point: AGC Output";
private static final String INSTRUMENT_LSM_DEMODULATOR_OUTPUT = "Tap Point: LSM Demodulator Output";
private static final String INSTRUMENT_QPSK_SLICER_OUTPUT = "Tap Point: QPSK Slicer Output";
private static final String INSTRUMENT_BASEBAND_FILTER_OUTPUT = "Tap Point: Baseband Filter Output";
private static final String INSTRUMENT_AGC_OUTPUT = "Tap Point: AGC Output";
private static final String INSTRUMENT_LSM_DEMODULATOR_OUTPUT = "Tap Point: LSM Demodulator Output";
private static final String INSTRUMENT_QPSK_SLICER_OUTPUT = "Tap Point: QPSK Slicer Output";
private List<TapGroup> mAvailableTaps;
private ComplexFIRFilter_CB_CB mBasebandFilter;
private ComplexBufferToStreamConverter mStreamConverter = new ComplexBufferToStreamConverter();
private ComplexFeedForwardGainControl mAGC =
new ComplexFeedForwardGainControl( 32 );
private LSMDemodulator mLSMDemodulator = new LSMDemodulator();
private QPSKPolarSlicer mQPSKSlicer = new QPSKPolarSlicer();
private P25MessageFramer mMessageFramer;
public P25_LSMDecoder( AliasList aliasList )
{
super( aliasList );
mBasebandFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass(
48000, 7250, 8000, 60, WindowType.HANN, true ), 1.0f );
mBasebandFilter.setListener( mStreamConverter );
mStreamConverter.setListener( mAGC );
private List<TapGroup> mAvailableTaps;
mAGC.setListener( mLSMDemodulator );
mLSMDemodulator.setSymbolListener( mQPSKSlicer );
mMessageFramer = new P25MessageFramer( aliasList, mLSMDemodulator );
mQPSKSlicer.addListener( mMessageFramer );
mMessageFramer.setListener( getMessageProcessor() );
}
public void dispose()
{
super.dispose();
mBasebandFilter.dispose();
mBasebandFilter = null;
mStreamConverter.dispose();
mStreamConverter = null;
mAGC.dispose();
mAGC = null;
private ComplexFIRFilter_CB_CB mBasebandFilter;
private ComplexBufferToStreamConverter mStreamConverter = new ComplexBufferToStreamConverter();
private ComplexFeedForwardGainControl mAGC =
new ComplexFeedForwardGainControl(32);
private LSMDemodulator mLSMDemodulator = new LSMDemodulator();
private QPSKPolarSlicer mQPSKSlicer = new QPSKPolarSlicer();
private P25MessageFramer mMessageFramer;
private ReusableBufferListener mReusableBufferListener = new ReusableBufferListener();
mLSMDemodulator.dispose();
mLSMDemodulator = null;
mQPSKSlicer.dispose();
mQPSKSlicer = null;
mMessageFramer.dispose();
mMessageFramer = null;
}
public P25_LSMDecoder(AliasList aliasList)
{
super(aliasList);
mBasebandFilter = new ComplexFIRFilter_CB_CB(FilterFactory.getLowPass(
48000, 7250, 8000, 60, WindowType.HANN, true), 1.0f);
mBasebandFilter.setListener(mStreamConverter);
mStreamConverter.setListener(mAGC);
mAGC.setListener(mLSMDemodulator);
mLSMDemodulator.setSymbolListener(mQPSKSlicer);
mMessageFramer = new P25MessageFramer(aliasList, mLSMDemodulator);
mQPSKSlicer.addListener(mMessageFramer);
mMessageFramer.setListener(getMessageProcessor());
}
public void dispose()
{
super.dispose();
mBasebandFilter.dispose();
mBasebandFilter = null;
mStreamConverter.dispose();
mStreamConverter = null;
mAGC.dispose();
mAGC = null;
mLSMDemodulator.dispose();
mLSMDemodulator = null;
mQPSKSlicer.dispose();
mQPSKSlicer = null;
mMessageFramer.dispose();
mMessageFramer = null;
}
@Override
public Listener<ComplexBuffer> getComplexBufferListener()
{
return mBasebandFilter;
}
public Listener<ReusableComplexBuffer> getReusableComplexBufferListener()
{
return mReusableBufferListener;
}
public Modulation getModulation()
{
return Modulation.CQPSK;
}
/**
* Provides a list of instrumentation taps for monitoring internal processing
*/
@Override
public Modulation getModulation()
{
return Modulation.CQPSK;
}
/**
* Provides a list of instrumentation taps for monitoring internal processing
*/
@Override
public List<TapGroup> getTapGroups()
{
if( mAvailableTaps == null )
{
mAvailableTaps = new ArrayList<>();
if(mAvailableTaps == null)
{
mAvailableTaps = new ArrayList<>();
TapGroup group = new TapGroup( "P25 LSM Decoder" );
group.add( new ComplexTap( INSTRUMENT_BASEBAND_FILTER_OUTPUT, 0, 1.0f ) );
group.add( new ComplexTap( INSTRUMENT_AGC_OUTPUT, 0, 1.0f ) );
group.add( new QPSKTap( INSTRUMENT_LSM_DEMODULATOR_OUTPUT, 0, 1.0f ) );
group.add( new DibitTap( INSTRUMENT_QPSK_SLICER_OUTPUT, 0, 0.1f ) );
TapGroup group = new TapGroup("P25 LSM Decoder");
mAvailableTaps.add( group );
if( mLSMDemodulator != null )
{
mAvailableTaps.addAll( mLSMDemodulator.getTapGroups() );
}
}
return mAvailableTaps;
group.add(new ComplexTap(INSTRUMENT_BASEBAND_FILTER_OUTPUT, 0, 1.0f));
group.add(new ComplexTap(INSTRUMENT_AGC_OUTPUT, 0, 1.0f));
group.add(new QPSKTap(INSTRUMENT_LSM_DEMODULATOR_OUTPUT, 0, 1.0f));
group.add(new DibitTap(INSTRUMENT_QPSK_SLICER_OUTPUT, 0, 0.1f));
mAvailableTaps.add(group);
if(mLSMDemodulator != null)
{
mAvailableTaps.addAll(mLSMDemodulator.getTapGroups());
}
}
return mAvailableTaps;
}
/**
* Adds the instrumentation tap
*/
@Override
public void registerTap( Tap tap )
/**
* Adds the instrumentation tap
*/
@Override
public void registerTap(Tap tap)
{
if( mLSMDemodulator != null )
{
mLSMDemodulator.registerTap( tap );
}
switch( tap.getName() )
{
case INSTRUMENT_BASEBAND_FILTER_OUTPUT:
ComplexSampleTap baseband = (ComplexSampleTap)tap;
mStreamConverter.setListener( baseband );
baseband.setListener( mAGC );
break;
case INSTRUMENT_AGC_OUTPUT:
ComplexSampleTap agcSymbol = (ComplexSampleTap)tap;
mAGC.setListener( agcSymbol );
agcSymbol.setListener( mLSMDemodulator );
break;
case INSTRUMENT_LSM_DEMODULATOR_OUTPUT:
QPSKTap qpsk = (QPSKTap)tap;
mLSMDemodulator.setSymbolListener( qpsk );
qpsk.setListener( mQPSKSlicer );
break;
case INSTRUMENT_QPSK_SLICER_OUTPUT:
mQPSKSlicer.addListener( (DibitTap)tap );
break;
default:
throw new IllegalArgumentException( "Unrecognized tap: " +
tap.getName() );
}
if(mLSMDemodulator != null)
{
mLSMDemodulator.registerTap(tap);
}
switch(tap.getName())
{
case INSTRUMENT_BASEBAND_FILTER_OUTPUT:
ComplexSampleTap baseband = (ComplexSampleTap)tap;
mStreamConverter.setListener(baseband);
baseband.setListener(mAGC);
break;
case INSTRUMENT_AGC_OUTPUT:
ComplexSampleTap agcSymbol = (ComplexSampleTap)tap;
mAGC.setListener(agcSymbol);
agcSymbol.setListener(mLSMDemodulator);
break;
case INSTRUMENT_LSM_DEMODULATOR_OUTPUT:
QPSKTap qpsk = (QPSKTap)tap;
mLSMDemodulator.setSymbolListener(qpsk);
qpsk.setListener(mQPSKSlicer);
break;
case INSTRUMENT_QPSK_SLICER_OUTPUT:
mQPSKSlicer.addListener((DibitTap)tap);
break;
default:
throw new IllegalArgumentException("Unrecognized tap: " +
tap.getName());
}
}
/**
* Removes the instrumentation tap
*/
@Override
public void unregisterTap( Tap tap )
/**
* Removes the instrumentation tap
*/
@Override
public void unregisterTap(Tap tap)
{
if( mLSMDemodulator != null )
{
mLSMDemodulator.unregisterTap( tap );
}
switch( tap.getName() )
{
case INSTRUMENT_BASEBAND_FILTER_OUTPUT:
mStreamConverter.setListener( mAGC );
break;
case INSTRUMENT_AGC_OUTPUT:
mAGC.setListener( mLSMDemodulator );
break;
case INSTRUMENT_LSM_DEMODULATOR_OUTPUT:
mLSMDemodulator.setSymbolListener( mQPSKSlicer );
break;
case INSTRUMENT_QPSK_SLICER_OUTPUT:
mQPSKSlicer.removeListener( (DibitTap)tap );
break;
default:
throw new IllegalArgumentException( "Unrecognized tap: " +
tap.getName() );
}
if(mLSMDemodulator != null)
{
mLSMDemodulator.unregisterTap(tap);
}
switch(tap.getName())
{
case INSTRUMENT_BASEBAND_FILTER_OUTPUT:
mStreamConverter.setListener(mAGC);
break;
case INSTRUMENT_AGC_OUTPUT:
mAGC.setListener(mLSMDemodulator);
break;
case INSTRUMENT_LSM_DEMODULATOR_OUTPUT:
mLSMDemodulator.setSymbolListener(mQPSKSlicer);
break;
case INSTRUMENT_QPSK_SLICER_OUTPUT:
mQPSKSlicer.removeListener((DibitTap)tap);
break;
default:
throw new IllegalArgumentException("Unrecognized tap: " +
tap.getName());
}
}
@Override
public Listener<SourceEvent> getSourceEventListener()
{
return null;
}
@Override
public Listener<SourceEvent> getSourceEventListener()
{
return null;
}
@Override
public void setSourceEventListener(Listener<SourceEvent> listener)
{
@Override
public void setSourceEventListener(Listener<SourceEvent> listener)
{
}
}
@Override
public void removeSourceEventListener()
{
@Override
public void removeSourceEventListener()
{
}
}
public class ReusableBufferListener implements Listener<ReusableComplexBuffer>
{
@Override
public void receive(ReusableComplexBuffer reusableComplexBuffer)
{
float[] samples = reusableComplexBuffer.getCopyOfSamples();
//TODO: redesign the filter chain so that we can simply pass a float array ...
mBasebandFilter.receive(new ComplexBuffer(samples));
reusableComplexBuffer.decrementUserCount();
}
}
}

View File

@@ -1,19 +1,17 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* sdr-trunk
* Copyright (C) 2014-2018 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 io.github.dsheirer.module.demodulate.am;
@@ -26,12 +24,12 @@ import io.github.dsheirer.dsp.gain.AutomaticGainControl_RB;
import io.github.dsheirer.module.Module;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.complex.ComplexBuffer;
import io.github.dsheirer.sample.complex.IComplexBufferListener;
import io.github.dsheirer.sample.complex.reusable.IReusableComplexBufferListener;
import io.github.dsheirer.sample.complex.reusable.ReusableComplexBuffer;
import io.github.dsheirer.sample.real.IFilteredRealBufferProvider;
import io.github.dsheirer.sample.real.RealBuffer;
public class AMDemodulatorModule extends Module
implements IComplexBufferListener, IFilteredRealBufferProvider
public class AMDemodulatorModule extends Module implements IReusableComplexBufferListener, IFilteredRealBufferProvider
{
private static final int SAMPLE_RATE = 48000;
@@ -39,6 +37,7 @@ public class AMDemodulatorModule extends Module
private AMDemodulator_CB mDemodulator;
private RealFIRFilter_RB_RB mLowPassFilter;
private AutomaticGainControl_RB mAGC;
private ReusableBufferListener mReusableBufferListener = new ReusableBufferListener();
/**
* AM Demodulator Module
@@ -60,9 +59,9 @@ public class AMDemodulatorModule extends Module
}
@Override
public Listener<ComplexBuffer> getComplexBufferListener()
public Listener<ReusableComplexBuffer> getReusableComplexBufferListener()
{
return mIQFilter;
return mReusableBufferListener;
}
@Override
@@ -107,4 +106,19 @@ public class AMDemodulatorModule extends Module
public void stop()
{
}
public class ReusableBufferListener implements Listener<ReusableComplexBuffer>
{
@Override
public void receive(ReusableComplexBuffer reusableComplexBuffer)
{
float[] samples = reusableComplexBuffer.getCopyOfSamples();
//TODO: redesign the filter chain so that we can simply pass a float array ...
mIQFilter.receive(new ComplexBuffer(samples));
reusableComplexBuffer.decrementUserCount();
}
}
}

View File

@@ -24,13 +24,14 @@ import io.github.dsheirer.dsp.fm.FMDemodulator_CB;
import io.github.dsheirer.module.Module;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.complex.ComplexBuffer;
import io.github.dsheirer.sample.complex.IComplexBufferListener;
import io.github.dsheirer.sample.complex.reusable.IReusableComplexBufferListener;
import io.github.dsheirer.sample.complex.reusable.ReusableComplexBuffer;
import io.github.dsheirer.sample.real.IUnFilteredRealBufferProvider;
import io.github.dsheirer.sample.real.RealBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FMDemodulatorModule extends Module implements IComplexBufferListener, IUnFilteredRealBufferProvider
public class FMDemodulatorModule extends Module implements IReusableComplexBufferListener, IUnFilteredRealBufferProvider
{
private final static Logger mLog = LoggerFactory.getLogger(FMDemodulatorModule.class);
@@ -38,6 +39,7 @@ public class FMDemodulatorModule extends Module implements IComplexBufferListene
private ComplexFIRFilter_CB_CB mIQFilter;
private FMDemodulator_CB mDemodulator;
private ReusableBufferListener mReusableBufferListener = new ReusableBufferListener();
/**
* FM Demodulator with I/Q filter. Demodulated output is unfiltered and
@@ -68,9 +70,9 @@ public class FMDemodulatorModule extends Module implements IComplexBufferListene
}
@Override
public Listener<ComplexBuffer> getComplexBufferListener()
public Listener<ReusableComplexBuffer> getReusableComplexBufferListener()
{
return mIQFilter;
return mReusableBufferListener;
}
@Override
@@ -110,4 +112,18 @@ public class FMDemodulatorModule extends Module implements IComplexBufferListene
public void stop()
{
}
public class ReusableBufferListener implements Listener<ReusableComplexBuffer>
{
@Override
public void receive(ReusableComplexBuffer reusableComplexBuffer)
{
float[] samples = reusableComplexBuffer.getCopyOfSamples();
//TODO: redesign the filter chain so that we can simply pass a float array ...
mIQFilter.receive(new ComplexBuffer(samples));
reusableComplexBuffer.decrementUserCount();
}
}
}