/******************************************************************************* * sdrtrunk * Copyright (C) 2014-2017 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 * ******************************************************************************/ package io.github.dsheirer.module; import io.github.dsheirer.audio.AudioPacket; import io.github.dsheirer.audio.IAudioPacketListener; import io.github.dsheirer.audio.IAudioPacketProvider; import io.github.dsheirer.audio.squelch.ISquelchStateListener; import io.github.dsheirer.audio.squelch.ISquelchStateProvider; import io.github.dsheirer.audio.squelch.SquelchState; import io.github.dsheirer.channel.metadata.AttributeChangeRequest; import io.github.dsheirer.channel.metadata.IAttributeChangeRequestListener; import io.github.dsheirer.channel.metadata.IAttributeChangeRequestProvider; import io.github.dsheirer.channel.state.ChannelState; import io.github.dsheirer.channel.state.DecoderState; import io.github.dsheirer.channel.state.DecoderStateEvent; import io.github.dsheirer.channel.state.IDecoderStateEventListener; import io.github.dsheirer.channel.state.IDecoderStateEventProvider; import io.github.dsheirer.controller.channel.Channel.ChannelType; import io.github.dsheirer.controller.channel.ChannelEvent; import io.github.dsheirer.controller.channel.IChannelEventListener; import io.github.dsheirer.controller.channel.IChannelEventProvider; import io.github.dsheirer.message.IMessageListener; import io.github.dsheirer.message.IMessageProvider; import io.github.dsheirer.message.Message; import io.github.dsheirer.module.decode.event.CallEvent; import io.github.dsheirer.module.decode.event.CallEventModel; import io.github.dsheirer.module.decode.event.ICallEventListener; import io.github.dsheirer.module.decode.event.ICallEventProvider; import io.github.dsheirer.module.decode.event.MessageActivityModel; import io.github.dsheirer.module.log.EventLogger; 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.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; import io.github.dsheirer.sample.real.IUnFilteredRealBufferProvider; import io.github.dsheirer.sample.real.RealBuffer; import io.github.dsheirer.source.ComplexSource; import io.github.dsheirer.source.ISourceEventListener; import io.github.dsheirer.source.ISourceEventProvider; import io.github.dsheirer.source.RealSource; import io.github.dsheirer.source.Source; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.heartbeat.Heartbeat; import io.github.dsheirer.source.heartbeat.IHeartbeatListener; import io.github.dsheirer.source.heartbeat.IHeartbeatProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; /** * Processing chain provides a framework for connecting a complex or real sample * source to a set of one primary decoder and zero or more auxiliary decoders. * All decoded messages and call events produced by the decoders and the decoder * call states are aggregated by the various broadcasters. You can register * listeners to receive aggregated messages, call events, and audio packets. * * Normal setup sequence: * * 1) Add one or more modules * 2) Register listeners to receive messages, call events, audio, etc. * 3) Add a valid source * 4) Invoke the start() method to start processing. * 5) Invoke the stop() method to stop processing. * * Optional: if you want to reuse the processing chain with a new sample source, * invoke the following method sequence: stop(), setSource(), start() */ public class ProcessingChain implements IChannelEventListener { private final static Logger mLog = LoggerFactory.getLogger(ProcessingChain.class); private Broadcaster mAttributeChangeRequestBroadcaster = new Broadcaster<>(); private Broadcaster mAudioPacketBroadcaster = new Broadcaster<>(); private Broadcaster mCallEventBroadcaster = new Broadcaster<>(); private Broadcaster mChannelEventBroadcaster = new Broadcaster<>(); private ReusableBufferBroadcaster mComplexBufferBroadcaster = new ReusableBufferBroadcaster(); private Broadcaster mDecoderStateEventBroadcaster = new Broadcaster<>(); private Broadcaster mHeartbeatBroadcaster = new Broadcaster<>(); private Broadcaster mSourceEventBroadcaster = new Broadcaster<>(); private Broadcaster mMessageBroadcaster = new Broadcaster<>(); private Broadcaster mFilteredRealBufferBroadcaster = new Broadcaster<>(); private Broadcaster mUnFilteredRealBufferBroadcaster = new Broadcaster<>(); private Broadcaster mSquelchStateBroadcaster = new Broadcaster<>(); private AtomicBoolean mRunning = new AtomicBoolean(); protected Source mSource; private List mModules = new ArrayList<>(); private CallEventModel mCallEventModel; private ChannelState mChannelState; private MessageActivityModel mMessageActivityModel; /** * Creates a processing chain for managing a set of modules * * @param channelType */ public ProcessingChain(ChannelType channelType) { mChannelState = new ChannelState(channelType); addModule(mChannelState); mCallEventModel = new CallEventModel(); addCallEventListener(mCallEventModel); } public CallEventModel getCallEventModel() { return mCallEventModel; } public ChannelState getChannelState() { return mChannelState; } public MessageActivityModel getMessageActivityModel() { return mMessageActivityModel; } public void setMessageActivityModel(MessageActivityModel model) { mMessageActivityModel = model; addMessageListener(mMessageActivityModel); } public void dispose() { stop(); for(Module module : mModules) { module.dispose(); } mModules.clear(); mAudioPacketBroadcaster.dispose(); mCallEventBroadcaster.dispose(); mChannelEventBroadcaster.dispose(); mComplexBufferBroadcaster.dispose(); mMessageBroadcaster.dispose(); mFilteredRealBufferBroadcaster.dispose(); mSquelchStateBroadcaster.dispose(); } /** * Indicates if this processing chain is currently receiving samples from * a source and sending those samples to the decoders. */ public boolean isProcessing() { return mRunning.get(); } /** * Indicates if this chain currently has a valid sample source. */ public boolean hasSource() { return mSource != null; } /** * Applies a sample source to this processing chain. Processing won't * start until the start() method is invoked. * * @param source - real or complex sample source * @throws IllegalStateException if the processing chain is currently * processing with another source. Invoke stop() before applying a new * source. */ public void setSource(Source source) throws IllegalStateException { if(isProcessing()) { throw new IllegalStateException("Processing chain is currently processing. Invoke stop() on the " + "processing chain before applying a new sample source"); } mSource = source; addModule(mSource); } /** * List of current modules for this processing chain */ public List getModules() { return mModules; } /** * List of decoder states for this processing chain */ public List getDecoderStates() { List decoderStates = new ArrayList<>(); for(Module module : mModules) { if(module instanceof DecoderState) { decoderStates.add((DecoderState) module); } } return decoderStates; } /** * Adds the list of modules to this processing chain */ public void addModules(List modules) { for(Module module : modules) { addModule(module); } } /** * Adds a module to the processing chain. Each module is tested for the * interfaces that it supports and is registered or receives a listener * to consume or produce the supported interface data type. All elements * and events that are produced by any module are automatically routed to * all other components that support the corresponding listener interface. * * At least one module should consume complex samples and either produce * decoded messages and/or audio, or produce decoded real sample buffers * for all other modules to consume. * * @param module - processing module, demodulator, decoder, source, state * machine, etc. */ public void addModule(Module module) { mModules.add(module); registerListeners(module); registerProviders(module); } /** * Removes the module from the processing chain and deregisters the module as a listener and * as a provider */ public void removeModule(Module module) { unregisterListeners(module); unregisterProviders(module); mModules.remove(module); } /** * Registers the module as a listener to each of the broadcasters that * provide the data interface(s) supported by the module. */ private void registerListeners(Module module) { if(module instanceof IAttributeChangeRequestListener) { mAttributeChangeRequestBroadcaster.addListener(((IAttributeChangeRequestListener)module).getAttributeChangeRequestListener()); } if(module instanceof IAudioPacketListener) { mAudioPacketBroadcaster.addListener(((IAudioPacketListener) module).getAudioPacketListener()); } if(module instanceof ICallEventListener) { mCallEventBroadcaster.addListener(((ICallEventListener) module).getCallEventListener()); } if(module instanceof IChannelEventListener) { mChannelEventBroadcaster.addListener(((IChannelEventListener) module).getChannelEventListener()); } if(module instanceof IDecoderStateEventListener) { mDecoderStateEventBroadcaster.addListener(((IDecoderStateEventListener) module).getDecoderStateListener()); } if(module instanceof IFilteredRealBufferListener) { mFilteredRealBufferBroadcaster.addListener(((IFilteredRealBufferListener) module).getFilteredRealBufferListener()); } if(module instanceof IHeartbeatListener) { mHeartbeatBroadcaster.addListener(((IHeartbeatListener)module).getHeartbeatListener()); } if(module instanceof IMessageListener) { mMessageBroadcaster.addListener(((IMessageListener) module).getMessageListener()); } if(module instanceof IReusableComplexBufferListener) { mComplexBufferBroadcaster.addListener(((IReusableComplexBufferListener) module).getReusableComplexBufferListener()); } if(module instanceof ISourceEventListener) { Listener listener = ((ISourceEventListener)module).getSourceEventListener(); if(listener != null) { mSourceEventBroadcaster.addListener(listener); } } if(module instanceof ISquelchStateListener) { mSquelchStateBroadcaster.addListener(((ISquelchStateListener) module).getSquelchStateListener()); } if(module instanceof IUnFilteredRealBufferListener) { mUnFilteredRealBufferBroadcaster.addListener(((IUnFilteredRealBufferListener) module).getUnFilteredRealBufferListener()); } } /** * Registers the module as a listener to each of the broadcasters that * provide the data interface(s) supported by the module. */ private void unregisterListeners(Module module) { if(module instanceof IAttributeChangeRequestListener) { mAttributeChangeRequestBroadcaster.removeListener(((IAttributeChangeRequestListener) module).getAttributeChangeRequestListener()); } if(module instanceof IAudioPacketListener) { mAudioPacketBroadcaster.removeListener(((IAudioPacketListener) module).getAudioPacketListener()); } if(module instanceof ICallEventListener) { mCallEventBroadcaster.removeListener(((ICallEventListener) module).getCallEventListener()); } if(module instanceof IChannelEventListener) { mChannelEventBroadcaster.removeListener(((IChannelEventListener) module).getChannelEventListener()); } if(module instanceof IDecoderStateEventListener) { mDecoderStateEventBroadcaster.removeListener(((IDecoderStateEventListener) module).getDecoderStateListener()); } if(module instanceof IFilteredRealBufferListener) { mFilteredRealBufferBroadcaster.removeListener(((IFilteredRealBufferListener) module).getFilteredRealBufferListener()); } if(module instanceof IHeartbeatListener) { 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 ISquelchStateListener) { mSquelchStateBroadcaster.removeListener(((ISquelchStateListener) module).getSquelchStateListener()); } if(module instanceof IUnFilteredRealBufferListener) { mUnFilteredRealBufferBroadcaster.removeListener(((IUnFilteredRealBufferListener) module).getUnFilteredRealBufferListener()); } } /** * Registers the broadcaster(s) as listeners to the module for each * provider interface that is supported by the module. */ private void registerProviders(Module module) { if(module instanceof IAttributeChangeRequestProvider) { ((IAttributeChangeRequestProvider)module).setAttributeChangeRequestListener(mAttributeChangeRequestBroadcaster); } if(module instanceof IAudioPacketProvider) { ((IAudioPacketProvider) module).setAudioPacketListener(mAudioPacketBroadcaster); } if(module instanceof ICallEventProvider) { ((ICallEventProvider) module).addCallEventListener(mCallEventBroadcaster); } if(module instanceof IChannelEventProvider) { ((IChannelEventProvider) module).setChannelEventListener(mChannelEventBroadcaster); } if(module instanceof IDecoderStateEventProvider) { ((IDecoderStateEventProvider) module).setDecoderStateListener(mDecoderStateEventBroadcaster); } if(module instanceof IHeartbeatProvider) { ((IHeartbeatProvider)module).getHeartbeatManager().addHeartbeatListener(mHeartbeatBroadcaster); } if(module instanceof IMessageProvider) { ((IMessageProvider) module).setMessageListener(mMessageBroadcaster); } if(module instanceof IFilteredRealBufferProvider) { ((IFilteredRealBufferProvider) module).setFilteredRealBufferListener(mFilteredRealBufferBroadcaster); } if(module instanceof IMessageProvider) { ((IMessageProvider) module).setMessageListener(mMessageBroadcaster); } if(module instanceof ISourceEventProvider) { ((ISourceEventProvider) module).setSourceEventListener(mSourceEventBroadcaster); } if(module instanceof ISquelchStateProvider) { ((ISquelchStateProvider) module).setSquelchStateListener(mSquelchStateBroadcaster); } if(module instanceof IUnFilteredRealBufferProvider) { ((IUnFilteredRealBufferProvider) module).setUnFilteredRealBufferListener(mUnFilteredRealBufferBroadcaster); } } /** * Unregisters the broadcaster(s) as listeners to the module for each * provider interface that is supported by the module. */ private void unregisterProviders(Module module) { if(module instanceof IAttributeChangeRequestProvider) { ((IAttributeChangeRequestProvider)module).removeAttributeChangeRequestListener(mAttributeChangeRequestBroadcaster); } if(module instanceof IAudioPacketProvider) { ((IAudioPacketProvider) module).setAudioPacketListener(null); } if(module instanceof ICallEventProvider) { ((ICallEventProvider) module).removeCallEventListener(mCallEventBroadcaster); } if(module instanceof IChannelEventProvider) { ((IChannelEventProvider) module).setChannelEventListener(null); } if(module instanceof IDecoderStateEventProvider) { ((IDecoderStateEventProvider) module).setDecoderStateListener(null); } if(module instanceof IHeartbeatProvider) { ((IHeartbeatProvider)module).getHeartbeatManager().removeHeartbeatListener(mHeartbeatBroadcaster); } if(module instanceof IMessageProvider) { ((IMessageProvider) module).setMessageListener(null); } if(module instanceof IFilteredRealBufferProvider) { ((IFilteredRealBufferProvider) module).setFilteredRealBufferListener(null); } if(module instanceof IMessageProvider) { ((IMessageProvider) module).setMessageListener(null); } if(module instanceof ISourceEventProvider) { ((ISourceEventProvider) module).setSourceEventListener(null); } if(module instanceof ISquelchStateProvider) { ((ISquelchStateProvider) module).setSquelchStateListener(null); } if(module instanceof IUnFilteredRealBufferProvider) { ((IUnFilteredRealBufferProvider) module).setUnFilteredRealBufferListener(null); } } /** * Starts processing if the chain has a valid source. Invocations on an * already started chain have no effect. */ public void start() { if(mRunning.compareAndSet(false, true)) { if(mSource != null) { /* Reset each of the modules */ for(Module module : mModules) { module.reset(); } //Setup the channel state to monitor source overflow conditions mSource.setOverflowListener(mChannelState); /* Register with the source to receive sample data. Setup a * timer task to process the buffer queues 50 times a second * (every 20 ms) */ switch(mSource.getSampleType()) { case COMPLEX: ((ComplexSource) mSource).setListener(mComplexBufferBroadcaster); break; case REAL: ((RealSource) mSource).setListener(mFilteredRealBufferBroadcaster); break; default: throw new IllegalArgumentException("Unrecognized source " + "sample type - cannot start processing chain"); } /* Start each of the modules */ for(Module module : mModules) { try { module.start(); } catch(Exception e) { mLog.error("Error starting module", e); } } } else { mLog.error("Source is null on start()"); } } } /** * Stops processing if the chain is currently processing. Invocations on an already stopped chain have no effect. */ public void stop() { if(mRunning.compareAndSet(true, false)) { /* Stop each of the modules */ for(Module module : mModules) { module.stop(); } if(mSource != null) { removeModule(mSource); mSource.setOverflowListener(null); switch(mSource.getSampleType()) { case COMPLEX: ((ComplexSource) mSource).removeListener(mComplexBufferBroadcaster); break; case REAL: ((RealSource) mSource).removeListener(mFilteredRealBufferBroadcaster); break; default: throw new IllegalArgumentException("Unrecognized source sample type - cannot start processing " + "chain"); } /* Release the source */ mSource.dispose(); mSource = null; } } } /** * Removes any logging modules that are currently registered with this processing chain */ public void removeEventLoggingModules() { List eventLoggingModules = new ArrayList<>(); for(Module module : mModules) { if(module instanceof EventLogger) { eventLoggingModules.add(module); } } for(Module eventLoggingModule : eventLoggingModules) { removeModule(eventLoggingModule); } } /** * Removes any recording modules that are currently registered with this processing chain */ public void removeRecordingModules() { List recordingModules = new ArrayList<>(); for(Module module : mModules) { if(module instanceof RealBufferWaveRecorder || module instanceof ComplexBufferWaveRecorder) { recordingModules.add(module); } } for(Module recordingModule : recordingModules) { removeModule(recordingModule); } } /** * Adds the listener to receive audio packets from all modules. */ public void addAudioPacketListener(Listener listener) { mAudioPacketBroadcaster.addListener(listener); } public void removeAudioPacketListener(Listener listener) { mAudioPacketBroadcaster.removeListener(listener); } /** * Adds the listener to receive call events from all modules. */ public void addCallEventListener(Listener listener) { mCallEventBroadcaster.addListener(listener); } public void removeCallEventListener(Listener listener) { mCallEventBroadcaster.removeListener(listener); } /** * Adds the listener to receive call events from all modules. */ public void addChannelEventListener(Listener listener) { mChannelEventBroadcaster.addListener(listener); } public void removeChannelEventListener(Listener listener) { mChannelEventBroadcaster.removeListener(listener); } /** * Adds the listener to receive decoder state events from decoder modules */ public void addDecoderStateEventListener(Listener listener) { mDecoderStateEventBroadcaster.addListener(listener); } public void removeDecoderStateEventListener(Listener listener) { mDecoderStateEventBroadcaster.removeListener(listener); } public Listener getDecoderStateEventListener() { return mDecoderStateEventBroadcaster; } /** * Adds the listener to receive decoded messages from all decoders. */ public void addMessageListener(Listener listener) { mMessageBroadcaster.addListener(listener); } /** * Adds the list of listeners to receive decoded messages from all decoders. */ public void addMessageListeners(List> listeners) { for(Listener listener : listeners) { mMessageBroadcaster.addListener(listener); } } public void removeMessageListener(Listener listener) { mMessageBroadcaster.removeListener(listener); } /** * Adds the listener to receive frequency change events from the processing chain * @param listener to receive events */ public void addFrequencyChangeListener(Listener listener) { mSourceEventBroadcaster.addListener(listener); } /** * Removes the listener from receiving frequency change events * @param listener to remove */ public void removeFrequencyChangeListener(Listener listener) { mSourceEventBroadcaster.removeListener(listener); } /** * Adds the listener to receive call events from all modules. */ public void addSquelchStateListener(Listener listener) { mSquelchStateBroadcaster.addListener(listener); } public void removeSquelchStateListener(Listener listener) { mSquelchStateBroadcaster.removeListener(listener); } public void addRealBufferListener(Listener listener) { mFilteredRealBufferBroadcaster.addListener(listener); } public void removeRealBufferListener(Listener listener) { mFilteredRealBufferBroadcaster.removeListener(listener); } @Override public Listener getChannelEventListener() { return mChannelEventBroadcaster; } }