diff --git a/src/main/java/io/github/dsheirer/audio/AudioSegmentBroadcaster.java b/src/main/java/io/github/dsheirer/audio/AudioSegmentBroadcaster.java index b7c54da0..800570de 100644 --- a/src/main/java/io/github/dsheirer/audio/AudioSegmentBroadcaster.java +++ b/src/main/java/io/github/dsheirer/audio/AudioSegmentBroadcaster.java @@ -39,7 +39,7 @@ public class AudioSegmentBroadcaster extends Broadcaster @Override public void broadcast(T audioSegment) { - for(Listener listener : mListeners) + for(Listener listener : getListeners()) { audioSegment.incrementConsumerCount(); listener.receive(audioSegment); diff --git a/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java b/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java index ac94e023..95f78578 100644 --- a/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java +++ b/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java @@ -194,7 +194,10 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor */ public void addListener(ISourceEventProcessor processor) { - mProcessors.add(processor); + if(!mProcessors.contains(processor)) + { + mProcessors.add(processor); + } } /** diff --git a/src/main/java/io/github/dsheirer/sample/Broadcaster.java b/src/main/java/io/github/dsheirer/sample/Broadcaster.java index 5ed2569d..6b51acba 100644 --- a/src/main/java/io/github/dsheirer/sample/Broadcaster.java +++ b/src/main/java/io/github/dsheirer/sample/Broadcaster.java @@ -22,8 +22,10 @@ package io.github.dsheirer.sample; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.locks.ReentrantLock; /** * Broadcasts an item to multiple listeners @@ -32,8 +34,8 @@ public class Broadcaster implements Listener { private final static Logger mLog = LoggerFactory.getLogger(Broadcaster.class); private boolean mDebug; - - protected List> mListeners = new CopyOnWriteArrayList<>(); + private ReentrantLock mLock = new ReentrantLock(); + private List> mListeners = new ArrayList<>(); public Broadcaster() { @@ -64,7 +66,16 @@ public class Broadcaster implements Listener */ public void dispose() { - mListeners.clear(); + mLock.lock(); + + try + { + mListeners.clear(); + } + finally + { + mLock.unlock(); + } } /** @@ -75,14 +86,6 @@ public class Broadcaster implements Listener return !mListeners.isEmpty(); } - /** - * Indicates if the listener is currently registered with this broadcaster - */ - public boolean hasListener(Listener listener) - { - return listener != null && mListeners.contains(listener); - } - /** * The count of listeners currently registered with this broadcaster */ @@ -96,7 +99,7 @@ public class Broadcaster implements Listener */ public List> getListeners() { - return mListeners; + return Collections.unmodifiableList(mListeners); } /** @@ -106,9 +109,21 @@ public class Broadcaster implements Listener */ public void addListener(Listener listener) { - if(listener != null && !mListeners.contains(listener)) + if(listener != null) { - mListeners.add(listener); + mLock.lock(); + + try + { + if(!mListeners.contains(listener)) + { + mListeners.add(listener); + } + } + finally + { + mLock.unlock(); + } } } @@ -119,7 +134,16 @@ public class Broadcaster implements Listener { if(listener != null) { - mListeners.remove(listener); + mLock.lock(); + + try + { + mListeners.remove(listener); + } + finally + { + mLock.unlock(); + } } } @@ -128,7 +152,16 @@ public class Broadcaster implements Listener */ public void clear() { - mListeners.clear(); + mLock.lock(); + + try + { + mListeners.clear(); + } + finally + { + mLock.unlock(); + } } /** @@ -136,21 +169,30 @@ public class Broadcaster implements Listener */ public void broadcast(T t) { - if(mDebug) + mLock.lock(); + + try { - for(Listener listener : mListeners) + if(mDebug) { - mLog.debug("Sending [" + t + "] to listener [" + listener.getClass() + "]"); - listener.receive(t); - mLog.debug("Finished sending to listener [" + listener.getClass() + "]"); + for(Listener listener : mListeners) + { + mLog.debug("Sending [" + t + "] to listener [" + listener.getClass() + "]"); + listener.receive(t); + mLog.debug("Finished sending to listener [" + listener.getClass() + "]"); + } + } + else + { + for(Listener listener : mListeners) + { + listener.receive(t); + } } } - else + finally { - for(Listener listener : mListeners) - { - listener.receive(t); - } + mLock.unlock(); } } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/Tuner.java b/src/main/java/io/github/dsheirer/source/tuner/Tuner.java index 6b27b69b..749047db 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/Tuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/Tuner.java @@ -110,10 +110,17 @@ public abstract class Tuner implements ISourceEventProcessor, ITunerErrorListene if(getChannelSourceManager() != null) { getChannelSourceManager().stopAllChannels(); + getChannelSourceManager().dispose(); + mChannelSourceManager = null; } broadcast(new TunerEvent(this, Event.NOTIFICATION_SHUTTING_DOWN)); getTunerController().stop(); + getTunerController().dispose(); + + mTunerEventBroadcaster.clear(); + mTunerFrequencyErrorMonitor = null; + mTunerErrorListener = null; } /** diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/TunerController.java index 7698efee..90671fb7 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerController.java @@ -49,13 +49,13 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, protected Broadcaster mNativeBufferBroadcaster = new Broadcaster(); protected FrequencyController mFrequencyController; private int mMiddleUnusableHalfBandwidth; - private double mUsableBandwidthPercentage; - private Listener mSourceEventListener; private int mMeasuredFrequencyError; + private double mUsableBandwidthPercentage; + private SourceEventListenerToProcessorAdapter mSourceEventListener; private NativeBufferWaveRecorder mRecorder; private ITunerErrorListener mTunerErrorListener; - public FrequencyErrorCorrectionManager mFrequencyErrorCorrectionManager; private DecimalFormat mFrequencyErrorPPMFormat = new DecimalFormat("0.0"); + private FrequencyErrorCorrectionManager mFrequencyErrorCorrectionManager; /** * Abstract tuner controller class. The tuner controller manages frequency bandwidth and currently tuned channels @@ -78,6 +78,15 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, return mFrequencyErrorCorrectionManager; } + protected void dispose() + { + getFrequencyErrorCorrectionManager().dispose(); + mNativeBufferBroadcaster.clear(); + mFrequencyController.dispose(); + mSourceEventListener.dispose(); + mTunerErrorListener = null; + } + /** * Perform startup operations * @throws SourceException if there is an error that makes this tuner controller unusable @@ -141,7 +150,6 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, setFrequency(config.getFrequency()); setFrequencyCorrection(config.getFrequencyCorrection()); getFrequencyErrorCorrectionManager().setEnabled(config.getAutoPPMCorrectionEnabled()); - } /** diff --git a/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyController.java b/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyController.java index 93b829c1..f28c3b26 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyController.java @@ -25,8 +25,9 @@ import io.github.dsheirer.source.SourceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.locks.ReentrantLock; public class FrequencyController { @@ -40,8 +41,8 @@ public class FrequencyController private double mFrequencyCorrection = 0.0d; private double mSampleRate = 0.0d; private boolean mLocked = false; - - private List mProcessors = new CopyOnWriteArrayList<>(); + private List mProcessors = new ArrayList<>(); + private ReentrantLock mLock = new ReentrantLock(); /** * Constructs an instance @@ -52,6 +53,15 @@ public class FrequencyController mTunable = tunable; } + /** + * Prepare for disposal of this instance. + */ + public void dispose() + { + mProcessors.clear(); + mTunable = null; + } + /** * Indicates if this frequency controller is locked by a remote process. A locked state indicates that neither * the frequency nor the sample rate should be changed. The remote process will typically be a downstream @@ -276,9 +286,18 @@ public class FrequencyController */ public void addListener(ISourceEventProcessor processor) { - if(!mProcessors.contains(processor)) + mLock.lock(); + + try { - mProcessors.add(processor); + if(!mProcessors.contains(processor)) + { + mProcessors.add(processor); + } + } + finally + { + mLock.unlock(); } } @@ -287,7 +306,16 @@ public class FrequencyController */ public void removeFrequencyChangeProcessor(ISourceEventProcessor processor) { - mProcessors.remove(processor); + mLock.lock(); + + try + { + mProcessors.remove(processor); + } + finally + { + mLock.unlock(); + } } @@ -318,9 +346,18 @@ public class FrequencyController public void broadcast(SourceEvent event) throws SourceException { - for(ISourceEventProcessor processor : mProcessors) + mLock.lock(); + + try { - processor.process(event); + for(ISourceEventProcessor processor : mProcessors) + { + processor.process(event); + } + } + finally + { + mLock.unlock(); } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java index 7bddf8f8..4a7541c0 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java @@ -36,6 +36,14 @@ public abstract class ChannelSourceManager implements ISourceEventProcessor { private Broadcaster mSourceEventBroadcaster = new Broadcaster<>(); + /** + * Prepare for disposal + */ + public void dispose() + { + mSourceEventBroadcaster.clear(); + } + /** * Sorted set of tuner channels being sourced by this source manager. Set is ordered by frequency lowest to highest */ diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/FrequencyErrorCorrectionManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/FrequencyErrorCorrectionManager.java index 7b5d68b1..58c1f988 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/FrequencyErrorCorrectionManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/FrequencyErrorCorrectionManager.java @@ -49,6 +49,15 @@ public class FrequencyErrorCorrectionManager mTunerController = tunerController; } + /** + * Dispose of this manager instance and nullify references. + */ + public void dispose() + { + setEnabled(false); + mTunerController = null; + } + /** * Enables or disables automatic frequency PPM correction. * @param enabled to true to turn on automatic PPM adjustments. @@ -80,7 +89,7 @@ public class FrequencyErrorCorrectionManager */ private void applyCorrection() { - if(mEnabled && mTunerController.hasMeasuredFrequencyError()) + if(mEnabled && mTunerController != null && mTunerController.hasMeasuredFrequencyError()) { double frequencyCorrection = mTunerController.getFrequencyCorrection(); frequencyCorrection -= mTunerController.getPPMFrequencyError(); diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java index 090d1091..426d568c 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java @@ -26,7 +26,6 @@ import io.github.dsheirer.source.tuner.TunerClass; import io.github.dsheirer.source.tuner.TunerFactory; import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.manager.FrequencyErrorCorrectionManager; import io.github.dsheirer.source.tuner.usb.USBTunerController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -300,16 +299,6 @@ public class RTL2832TunerController extends USBTunerController return null; } - /** - * Manager for applying automatic frequency error PPM adjustments to the tuner controller based on - * frequency error measurements received from certain downstream decoders (e.g. P25). - * @return manager - */ - public FrequencyErrorCorrectionManager getFrequencyErrorCorrectionManager() - { - return mFrequencyErrorCorrectionManager; - } - /** * Overrides updates for measured frequency error so that the updates can also be applied to the * frequency error correction manager for automatic PPM updating. @@ -348,11 +337,11 @@ public class RTL2832TunerController extends USBTunerController { if(hasEmbeddedTuner()) { - return TunerClass.RTL2832.toString() + "/" + getTunerType().getLabel() + " " + mDescriptor.getSerial(); + return TunerClass.RTL2832 + "/" + getTunerType().getLabel() + " " + mDescriptor.getSerial(); } else { - return TunerClass.RTL2832.toString() + " " + mDescriptor.getSerial(); + return TunerClass.RTL2832 + " " + mDescriptor.getSerial(); } } else diff --git a/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerEditor.java index 63da401a..8bf71c9f 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerEditor.java @@ -99,6 +99,8 @@ public class DiscoveredTunerEditor extends Editor implements ID super.setItem(tuner); + mEditorScroller.remove(mEditor); + if(hasItem()) { mEditor = TunerFactory.getEditor(mUserPreferences, getItem(), mTunerManager); diff --git a/src/main/java/io/github/dsheirer/source/tuner/ui/TunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/ui/TunerEditor.java index 0f40c673..e7e8a079 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/ui/TunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/ui/TunerEditor.java @@ -116,7 +116,7 @@ public abstract class TunerEditor } } - /** + /**r * Indicates if the controls are currently being loaded with values. */ protected boolean isLoading() @@ -337,7 +337,7 @@ public abstract class TunerEditor if(mEnabledButton == null) { mEnabledButton = new JButton(BUTTON_STATUS_ENABLE); - mEnabledButton.setToolTipText("Enable or disable the tuner for use by sdrtrunk, or attempt to reset a tuner error"); + mEnabledButton.setToolTipText("Enable or disable the tuner for use by sdrtrunk"); mEnabledButton.addActionListener(e -> { switch(getEnabledButton().getText()) @@ -493,6 +493,11 @@ public abstract class TunerEditor */ public void dispose() { + turnOffRecorder(); + + getFrequencyControl().clearListeners(); + getFrequencyCorrectionSpinner().removeChangeListener(mFrequencyAndCorrectionChangeListener); + if(mDiscoveredTuner != null) { mDiscoveredTuner.removeTunerStatusListener(this); @@ -501,9 +506,12 @@ public abstract class TunerEditor { mDiscoveredTuner.getTuner().removeTunerEventListener(this); } + + mDiscoveredTuner = null; } - turnOffRecorder(); + mUserPreferences = null; + mTunerManager = null; } /**