#1723 Initial commit - audio call management feature.

-Implements Spring Boot and spring dependency injection - significant code refactoring.
-Creates database repository with Calls table
-Implements AudioManager to update database and manage audio recordings.
-Calls paging and searching
-Adds audio recording to wave file and places file in the calls database table.
-Audio spectrum view with 64 bins.
-Updates audio playback manager to process the incoming calls queue and support auto, auto-locked, replay, and mute playback modes.
-Adds channel lock/unlock and mute/unmute features.
-Adds column configuration editor to change column order & visibility and adds table state monitoring to save any user customization of the column ordering.
-Adds call table column sort ordering to the saved table state and restores the sort on application launch.
-Adds audio playback channel view options for: Complete, Standard and Minimal.  Updates user preferences Audio | Playback section to allow user to select preferred view style.
This commit is contained in:
Dennis Sheirer
2023-11-26 08:15:25 -05:00
parent 42e7a49d37
commit af2b80a066
206 changed files with 10859 additions and 3904 deletions

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 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/>
* * *****************************************************************************
* 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.controller.channel;
@@ -26,29 +23,32 @@ import io.github.dsheirer.controller.channel.Channel.ChannelType;
import io.github.dsheirer.controller.channel.ChannelEvent.Event;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.springframework.stereotype.Component;
/**
* Channel Model
*/
@Component("channelModel")
public class ChannelModel implements Listener<ChannelEvent>
{
private ObservableList<Channel> mChannels = FXCollections.observableArrayList(Channel.extractor());
private ObservableList<Channel> mTrafficChannels = FXCollections.observableArrayList(Channel.extractor());
private Broadcaster<ChannelEvent> mChannelEventBroadcaster = new Broadcaster();
@Resource
private AliasModel mAliasModel;
public ChannelModel(AliasModel aliasModel)
/**
* Constructs an instance
*/
public ChannelModel()
{
mAliasModel = aliasModel;
//Register a listener to detect channel changes and broadcast change events to cause playlist save requests
ChannelListChangeListener changeListener = new ChannelListChangeListener();
mChannels.addListener(changeListener);

View File

@@ -20,19 +20,20 @@ package io.github.dsheirer.controller.channel;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.audio.AudioSegment;
import io.github.dsheirer.audio.AudioManager;
import io.github.dsheirer.audio.call.AudioSegment;
import io.github.dsheirer.channel.metadata.ChannelAndMetadata;
import io.github.dsheirer.channel.metadata.ChannelMetadata;
import io.github.dsheirer.channel.metadata.ChannelMetadataModel;
import io.github.dsheirer.controller.channel.event.ChannelStartProcessingRequest;
import io.github.dsheirer.controller.channel.event.ChannelStopProcessingRequest;
import io.github.dsheirer.controller.channel.event.PreloadDataContent;
import io.github.dsheirer.controller.channel.map.ChannelMapModel;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierClass;
import io.github.dsheirer.identifier.IdentifierUpdateNotification;
import io.github.dsheirer.identifier.decoder.DecoderLogicalChannelNameIdentifier;
import io.github.dsheirer.map.MapService;
import io.github.dsheirer.module.Module;
import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.module.decode.DecoderFactory;
@@ -50,6 +51,8 @@ import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency;
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import io.github.dsheirer.util.ThreadPool;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.awt.GraphicsEnvironment;
import java.util.ArrayList;
import java.util.List;
@@ -63,6 +66,7 @@ import java.util.concurrent.locks.ReentrantLock;
import javafx.application.Platform;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* Channel processing manager handles all starting and stopping of channel decoding. A processing chain is created
@@ -70,53 +74,49 @@ import org.slf4j.LoggerFactory;
* channel and protocol along with all logging and baseband or bitstream recording. Audio recording is handled outside
* of this class by the RecorderManager.
*/
@Component("channelProcessingManager")
public class ChannelProcessingManager implements Listener<ChannelEvent>
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelProcessingManager.class);
private static final String TUNER_UNAVAILABLE_DESCRIPTION = "TUNER UNAVAILABLE";
private Map<Channel,ProcessingChain> mProcessingChains = new ConcurrentHashMap<>();
private Lock mLock = new ReentrantLock();
private ChannelSourceEventErrorListener mSourceErrorListener = new ChannelSourceEventErrorListener();
private List<Listener<AudioSegment>> mAudioSegmentListeners = new CopyOnWriteArrayList<>();
private List<Listener<IDecodeEvent>> mDecodeEventListeners = new CopyOnWriteArrayList<>();
private Broadcaster<ChannelEvent> mChannelEventBroadcaster = new Broadcaster();
private ChannelMapModel mChannelMapModel;
private ChannelMetadataModel mChannelMetadataModel;
private EventLogManager mEventLogManager;
private TunerManager mTunerManager;
private AliasModel mAliasModel;
private UserPreferences mUserPreferences;
private List<Long> mLoggedFrequencies = new ArrayList<>();
private List<ScheduledFuture<?>> mDelayedChannelStartTasks = new ArrayList<>();
/**
* Constructs the channel processing manager
*
* @param channelMapModel containing channel maps defined by the user
* @param eventLogManager for adding event loggers to channels
* @param tunerManager for obtaining a tuner channel source for the channel
* @param aliasModel for aliasing of identifiers produced by the channel
* @param userPreferences for user defined behavior and settings
*/
public ChannelProcessingManager(ChannelMapModel channelMapModel, EventLogManager eventLogManager,
TunerManager tunerManager, AliasModel aliasModel, UserPreferences userPreferences)
{
mChannelMapModel = channelMapModel;
mEventLogManager = eventLogManager;
mTunerManager = tunerManager;
mAliasModel = aliasModel;
mUserPreferences = userPreferences;
mChannelMetadataModel = new ChannelMetadataModel();
}
@Resource
private AudioManager mAudioManager;
@Resource
private AliasModel mAliasModel;
@Resource
private ChannelMetadataModel mChannelMetadataModel;
@Resource
private DecoderFactory mDecoderFactory;
@Resource
private EventLogManager mEventLogManager;
@Resource
private MapService mMapService;
@Resource
private TunerManager mTunerManager;
@Resource
private UserPreferences mUserPreferences;
/**
* Channel metadata model containing metadata for each channel or channel time-slice that is currently processing.
* Constructs the channel processing manager
*/
public ChannelMetadataModel getChannelMetadataModel()
public ChannelProcessingManager()
{
return mChannelMetadataModel;
}
@PostConstruct
public void postConstruct()
{
addAudioSegmentListener(mAudioManager);
addDecodeEventListener(mMapService);
}
/**
@@ -398,7 +398,7 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
throw new ChannelException("No Tuner Available");
}
ProcessingChain processingChain = new ProcessingChain(channel, mAliasModel);
ProcessingChain processingChain = new ProcessingChain(mAliasModel, channel);
//Certain decoders aggregate the decode events in the parent channel that also includes any events produced
//by the traffic channels. Establish listener registration depending on if this channel is a traffic channel
@@ -441,8 +441,8 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
processingChain.addFrequencyChangeListener(channel);
/* Processing Modules */
List<Module> modules = DecoderFactory.getModules(mChannelMapModel, channel, mAliasModel, mUserPreferences,
request.getTrafficChannelManager(), request.getChannelDescriptor());
List<Module> modules = mDecoderFactory.getModules(channel, request.getTrafficChannelManager(),
request.getChannelDescriptor());
processingChain.addModules(modules);
//Post preload data from the request to the event bus. Modules that can handle preload data will annotate
@@ -560,7 +560,7 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
{
added = true;
mProcessingChains.put(channel, processingChain);
getChannelMetadataModel().add(new ChannelAndMetadata(channel, processingChain.getChannelState().getChannelMetadata()));
mChannelMetadataModel.add(new ChannelAndMetadata(channel, processingChain.getChannelState().getChannelMetadata()));
}
}
finally
@@ -590,7 +590,7 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
{
for(ChannelMetadata channelMetadata: removed.getChannelState().getChannelMetadata())
{
getChannelMetadataModel().remove(channelMetadata);
mChannelMetadataModel.remove(channelMetadata);
}
}
}

View File

@@ -19,20 +19,27 @@
package io.github.dsheirer.controller.channel;
import io.github.dsheirer.sample.Listener;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
public class ChannelSelectionManager implements Listener<ChannelEvent>
{
private Listener<ChannelEvent> mChannelEventListener;
@Resource
private ChannelModel mChannelModel;
private Channel mSelectedChannel = null;
/**
* Manages channel selection state to ensure that only one channel is ever
* in a selected state.
*/
public ChannelSelectionManager(ChannelModel channelModel)
public ChannelSelectionManager()
{
mChannelEventListener = channelModel;
channelModel.addListener(ChannelSelectionManager.this);
}
@PostConstruct
public void postConstruct()
{
mChannelModel.addListener(ChannelSelectionManager.this);
}
@Override
@@ -44,13 +51,13 @@ public class ChannelSelectionManager implements Listener<ChannelEvent>
if(mSelectedChannel != null)
{
mSelectedChannel.setSelected(false);
mChannelEventListener.receive(new ChannelEvent(mSelectedChannel, ChannelEvent.Event.NOTIFICATION_SELECTION_CHANGE));
mChannelModel.receive(new ChannelEvent(mSelectedChannel, ChannelEvent.Event.NOTIFICATION_SELECTION_CHANGE));
mSelectedChannel = null;
}
mSelectedChannel = event.getChannel();
mSelectedChannel.setSelected(true);
mChannelEventListener.receive(new ChannelEvent(event.getChannel(), ChannelEvent.Event.NOTIFICATION_SELECTION_CHANGE));
mChannelModel.receive(new ChannelEvent(event.getChannel(), ChannelEvent.Event.NOTIFICATION_SELECTION_CHANGE));
break;
case NOTIFICATION_PROCESSING_STOP:
case REQUEST_DESELECT:
@@ -59,7 +66,7 @@ public class ChannelSelectionManager implements Listener<ChannelEvent>
{
mSelectedChannel.setSelected(false);
mSelectedChannel = null;
mChannelEventListener.receive(new ChannelEvent(event.getChannel(), ChannelEvent.Event.NOTIFICATION_SELECTION_CHANGE));
mChannelModel.receive(new ChannelEvent(event.getChannel(), ChannelEvent.Event.NOTIFICATION_SELECTION_CHANGE));
}
break;
default:

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 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/>
* * *****************************************************************************
* 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.controller.channel.map;
@@ -25,15 +22,17 @@ package io.github.dsheirer.controller.channel.map;
import io.github.dsheirer.controller.channel.map.ChannelMapEvent.Event;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import java.util.ArrayList;
import java.util.List;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.swing.AbstractListModel;
import java.util.ArrayList;
import java.util.List;
@Component("channelMapModel")
public class ChannelMapModel extends AbstractListModel<ChannelMap>
{
private static final long serialVersionUID = 1L;