mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Merge pull request #1773 from DSheirer/1772-patch-group-streaming
1772 Patch Group Streaming Preference (Patch Group vs Individual Talkgroups)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -28,12 +28,9 @@ import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.duplicate.DuplicateCallDetectionPreference;
|
||||
import io.github.dsheirer.preference.duplicate.CallManagementPreference;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -42,6 +39,8 @@ import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Detects duplicate calls that occur within the same system. This detector is thread safe for the receive() method.
|
||||
@@ -52,18 +51,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
public class DuplicateCallDetector implements Listener<AudioSegment>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(DuplicateCallDetector.class);
|
||||
private DuplicateCallDetectionPreference mDuplicateCallDetectionPreference;
|
||||
private CallManagementPreference mCallManagementPreference;
|
||||
private Map<String,SystemDuplicateCallDetector> mDetectorMap = new HashMap();
|
||||
|
||||
public DuplicateCallDetector(UserPreferences userPreferences)
|
||||
{
|
||||
mDuplicateCallDetectionPreference = userPreferences.getDuplicateCallDetectionPreference();
|
||||
mCallManagementPreference = userPreferences.getDuplicateCallDetectionPreference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(AudioSegment audioSegment)
|
||||
{
|
||||
if(mDuplicateCallDetectionPreference.isDuplicateCallDetectionEnabled())
|
||||
if(mCallManagementPreference.isDuplicateCallDetectionEnabled())
|
||||
{
|
||||
Identifier identifier = audioSegment.getIdentifierCollection()
|
||||
.getIdentifier(IdentifierClass.CONFIGURATION, Form.SYSTEM, Role.ANY);
|
||||
@@ -137,7 +136,7 @@ public class DuplicateCallDetector implements Listener<AudioSegment>
|
||||
*/
|
||||
private boolean isDuplicate(AudioSegment segment1, AudioSegment segment2)
|
||||
{
|
||||
if(mDuplicateCallDetectionPreference.isDuplicateCallDetectionByTalkgroupEnabled())
|
||||
if(mCallManagementPreference.isDuplicateCallDetectionByTalkgroupEnabled())
|
||||
{
|
||||
//Step 1 check for duplicate TO values
|
||||
List<Identifier> to1 = segment1.getIdentifierCollection().getIdentifiers(Role.TO);
|
||||
@@ -149,7 +148,7 @@ public class DuplicateCallDetector implements Listener<AudioSegment>
|
||||
}
|
||||
}
|
||||
|
||||
if(mDuplicateCallDetectionPreference.isDuplicateCallDetectionByRadioEnabled())
|
||||
if(mCallManagementPreference.isDuplicateCallDetectionByRadioEnabled())
|
||||
{
|
||||
//Step 2 check for duplicate FROM values
|
||||
List<Identifier> from1 = segment1.getIdentifierCollection().getIdentifiers(Role.FROM);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -19,25 +19,34 @@
|
||||
|
||||
package io.github.dsheirer.audio.broadcast;
|
||||
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.audio.AudioSegment;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.MutableIdentifierCollection;
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.identifier.patch.PatchGroup;
|
||||
import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.record.AudioSegmentRecorder;
|
||||
import io.github.dsheirer.record.RecordFormat;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import io.github.dsheirer.util.TimeStamp;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Audio streaming manager monitors audio segments through completion and creates temporary streaming recordings on
|
||||
@@ -138,29 +147,60 @@ public class AudioStreamingManager implements Listener<AudioSegment>
|
||||
|
||||
if(mAudioRecordingListener != null && audioSegment.hasBroadcastChannels())
|
||||
{
|
||||
Path path = getTemporaryRecordingPath();
|
||||
long length = 0;
|
||||
|
||||
for(float[] audioBuffer: audioSegment.getAudioBuffers())
|
||||
{
|
||||
length += audioBuffer.length;
|
||||
}
|
||||
|
||||
length /= 8; //Sample rate is 8000 samples per second, or 8 samples per millisecond.
|
||||
|
||||
try
|
||||
{
|
||||
AudioSegmentRecorder.record(audioSegment, path, RecordFormat.MP3, mUserPreferences);
|
||||
IdentifierCollection identifierCollectionCopy =
|
||||
IdentifierCollection identifiers =
|
||||
new IdentifierCollection(audioSegment.getIdentifierCollection().getIdentifiers());
|
||||
|
||||
AudioRecording audioRecording = new AudioRecording(path, audioSegment.getBroadcastChannels(),
|
||||
identifierCollectionCopy, audioSegment.getStartTimestamp(), length);
|
||||
mAudioRecordingListener.receive(audioRecording);
|
||||
}
|
||||
catch(IOException ioe)
|
||||
if(identifiers.getToIdentifier() instanceof PatchGroupIdentifier patchGroupIdentifier)
|
||||
{
|
||||
mLog.error("Error recording temporary stream MP3");
|
||||
if(mUserPreferences.getDuplicateCallDetectionPreference()
|
||||
.getPatchGroupStreamingOption() == PatchGroupStreamingOption.TALKGROUPS)
|
||||
{
|
||||
//Decompose the patch group into the individual (patched) talkgroups and process the audio
|
||||
//segment for each patched talkgroup.
|
||||
PatchGroup patchGroup = patchGroupIdentifier.getValue();
|
||||
|
||||
List<Identifier> ids = new ArrayList<>();
|
||||
ids.addAll(patchGroup.getPatchedTalkgroupIdentifiers());
|
||||
ids.addAll(patchGroup.getPatchedRadioIdentifiers());
|
||||
|
||||
//If there are no patched radios/talkgroups, override user preference and stream as a patch group
|
||||
if(ids.isEmpty() || audioSegment.getAliasList() == null)
|
||||
{
|
||||
processAudioSegment(audioSegment, identifiers, audioSegment.getBroadcastChannels());
|
||||
}
|
||||
else
|
||||
{
|
||||
AliasList aliasList = audioSegment.getAliasList();
|
||||
|
||||
for(Identifier identifier: ids)
|
||||
{
|
||||
List<Alias> aliases = aliasList.getAliases(identifier);
|
||||
Set<BroadcastChannel> broadcastChannels = new HashSet<>();
|
||||
for(Alias alias: aliases)
|
||||
{
|
||||
broadcastChannels.addAll(alias.getBroadcastChannels());
|
||||
}
|
||||
|
||||
if(!broadcastChannels.isEmpty())
|
||||
{
|
||||
MutableIdentifierCollection decomposedIdentifiers =
|
||||
new MutableIdentifierCollection(identifiers.getIdentifiers());
|
||||
//Remove patch group TO identifier & replace with the patched talkgroup/radio
|
||||
decomposedIdentifiers.remove(Role.TO);
|
||||
decomposedIdentifiers.update(identifier);
|
||||
processAudioSegment(audioSegment, decomposedIdentifiers, broadcastChannels);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
processAudioSegment(audioSegment, identifiers, audioSegment.getBroadcastChannels());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
processAudioSegment(audioSegment, identifiers, audioSegment.getBroadcastChannels());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +209,40 @@ public class AudioStreamingManager implements Listener<AudioSegment>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an audio segment for streaming by creating a temporary MP3 recording and submitting the recording
|
||||
* to the specific broadcast channel(s).
|
||||
* @param audioSegment to process for streaming
|
||||
* @param identifierCollection to use for the streamed audio recording.
|
||||
* @param broadcastChannels to receive the audio recording
|
||||
*/
|
||||
private void processAudioSegment(AudioSegment audioSegment, IdentifierCollection identifierCollection,
|
||||
Set<BroadcastChannel> broadcastChannels)
|
||||
{
|
||||
Path path = getTemporaryRecordingPath();
|
||||
long length = 0;
|
||||
|
||||
for(float[] audioBuffer: audioSegment.getAudioBuffers())
|
||||
{
|
||||
length += audioBuffer.length;
|
||||
}
|
||||
|
||||
length /= 8; //Sample rate is 8000 samples per second, or 8 samples per millisecond.
|
||||
|
||||
try
|
||||
{
|
||||
AudioSegmentRecorder.record(audioSegment, path, RecordFormat.MP3, mUserPreferences, identifierCollection);
|
||||
|
||||
AudioRecording audioRecording = new AudioRecording(path, broadcastChannels, identifierCollection,
|
||||
audioSegment.getStartTimestamp(), length);
|
||||
mAudioRecordingListener.receive(audioRecording);
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
mLog.error("Error recording temporary stream MP3");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary streaming recording file path
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.audio.broadcast;
|
||||
|
||||
/**
|
||||
* Options for streaming management of patch groups. These options are used when streaming an audio call that is
|
||||
* tagged to a patch group. The audio will either be streamed once and identified as the patch group, or it will
|
||||
* be streamed multiple times, once for each individual patched talkgroup.
|
||||
*/
|
||||
public enum PatchGroupStreamingOption
|
||||
{
|
||||
PATCH_GROUP("Patch Group"),
|
||||
TALKGROUPS("Individual Talkgroups");
|
||||
|
||||
private String mLabel;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param label to display
|
||||
*/
|
||||
PatchGroupStreamingOption(String label)
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,7 @@ public class MBECallSequenceConverter
|
||||
|
||||
try
|
||||
{
|
||||
AudioSegmentRecorder.recordWAVE(audioSegment, outputPath);
|
||||
AudioSegmentRecorder.recordWAVE(audioSegment, outputPath, audioSegment.getIdentifierCollection());
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
|
||||
@@ -21,9 +21,9 @@ package io.github.dsheirer.gui.preference;
|
||||
|
||||
import io.github.dsheirer.gui.preference.application.ApplicationPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.calibration.VectorCalibrationPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.call.CallManagementPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.decoder.JmbeLibraryPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.directory.DirectoryPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.duplicate.DuplicateCallPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.mp3.MP3PreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.playback.PlaybackPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.record.RecordPreferenceEditor;
|
||||
@@ -42,8 +42,8 @@ public class PreferenceEditorFactory
|
||||
{
|
||||
case APPLICATION:
|
||||
return new ApplicationPreferenceEditor(userPreferences);
|
||||
case AUDIO_DUPLICATE_CALL_DETECTION:
|
||||
return new DuplicateCallPreferenceEditor(userPreferences);
|
||||
case AUDIO_CALL_MANAGEMENT:
|
||||
return new CallManagementPreferenceEditor(userPreferences);
|
||||
case AUDIO_MP3:
|
||||
return new MP3PreferenceEditor(userPreferences);
|
||||
case AUDIO_OUTPUT:
|
||||
|
||||
@@ -31,7 +31,7 @@ public enum PreferenceEditorType
|
||||
AUDIO_MP3("MP3"),
|
||||
AUDIO_RECORD("Record"),
|
||||
AUDIO_OUTPUT("Output/Tones"),
|
||||
AUDIO_DUPLICATE_CALL_DETECTION("Duplicate Calls"),
|
||||
AUDIO_CALL_MANAGEMENT("Call Management"),
|
||||
SOURCE_TUNERS("Tuners"),
|
||||
TALKGROUP_FORMAT("Talkgroup & Radio ID"),
|
||||
VECTOR_CALIBRATION("Vector Calibration"),
|
||||
|
||||
@@ -171,7 +171,7 @@ public class UserPreferencesEditor extends BorderPane
|
||||
applicationItem.setExpanded(true);
|
||||
|
||||
TreeItem<String> audioItem = new TreeItem<>("Audio");
|
||||
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_DUPLICATE_CALL_DETECTION));
|
||||
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_CALL_MANAGEMENT));
|
||||
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_MP3));
|
||||
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_OUTPUT));
|
||||
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_RECORD));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -17,12 +17,16 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.gui.preference.duplicate;
|
||||
package io.github.dsheirer.gui.preference.call;
|
||||
|
||||
import io.github.dsheirer.audio.broadcast.PatchGroupStreamingOption;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.duplicate.DuplicateCallDetectionPreference;
|
||||
import io.github.dsheirer.preference.duplicate.CallManagementPreference;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Separator;
|
||||
import javafx.scene.layout.GridPane;
|
||||
@@ -33,23 +37,24 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Preference settings for duplicate call audio handling
|
||||
* Preference settings for audio call management for duplicate calls and patch group streaming.
|
||||
*/
|
||||
public class DuplicateCallPreferenceEditor extends HBox
|
||||
public class CallManagementPreferenceEditor extends HBox
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(DuplicateCallPreferenceEditor.class);
|
||||
private DuplicateCallDetectionPreference mPreference;
|
||||
private final static Logger mLog = LoggerFactory.getLogger(CallManagementPreferenceEditor.class);
|
||||
private CallManagementPreference mPreference;
|
||||
private GridPane mEditorPane;
|
||||
private ToggleSwitch mDetectDuplicateTalkgroups;
|
||||
private ToggleSwitch mDetectDuplicateRadios;
|
||||
private ToggleSwitch mSuppressDuplicateListening;
|
||||
private ToggleSwitch mSuppressDuplicateRecording;
|
||||
private ToggleSwitch mSuppressDuplicateStreaming;
|
||||
private ComboBox<PatchGroupStreamingOption> mPatchGroupStreamingOptionComboBox;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public DuplicateCallPreferenceEditor(UserPreferences userPreferences)
|
||||
public CallManagementPreferenceEditor(UserPreferences userPreferences)
|
||||
{
|
||||
mPreference = userPreferences.getDuplicateCallDetectionPreference();
|
||||
|
||||
@@ -134,6 +139,19 @@ public class DuplicateCallPreferenceEditor extends HBox
|
||||
streamingLabel.setWrapText(true);
|
||||
GridPane.setConstraints(streamingLabel, 1, row);
|
||||
mEditorPane.getChildren().add(streamingLabel);
|
||||
|
||||
Separator separator2 = new Separator();
|
||||
GridPane.setHgrow(separator2, Priority.ALWAYS);
|
||||
GridPane.setConstraints(separator2, 0, ++row, 2, 1);
|
||||
mEditorPane.getChildren().add(separator2);
|
||||
|
||||
Label patchGroupLabel = new Label("Patch Group Streaming. Stream a patch group call as:");
|
||||
patchGroupLabel.setWrapText(true);
|
||||
GridPane.setConstraints(patchGroupLabel, 0, ++row, 2, 1);
|
||||
mEditorPane.getChildren().add(patchGroupLabel);
|
||||
|
||||
GridPane.setConstraints(getPatchGroupStreamingOptionComboBox(), 0, ++row, 2, 1);
|
||||
mEditorPane.getChildren().add(getPatchGroupStreamingOptionComboBox());
|
||||
}
|
||||
|
||||
return mEditorPane;
|
||||
@@ -212,4 +230,23 @@ public class DuplicateCallPreferenceEditor extends HBox
|
||||
|
||||
return mSuppressDuplicateStreaming;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combo box for presenting the patch group streaming options.
|
||||
* @return combo box.
|
||||
*/
|
||||
private ComboBox<PatchGroupStreamingOption> getPatchGroupStreamingOptionComboBox()
|
||||
{
|
||||
if(mPatchGroupStreamingOptionComboBox == null)
|
||||
{
|
||||
ObservableList<PatchGroupStreamingOption> options = FXCollections.observableArrayList();
|
||||
options.addAll(PatchGroupStreamingOption.values());
|
||||
mPatchGroupStreamingOptionComboBox = new ComboBox<>(options);
|
||||
mPatchGroupStreamingOptionComboBox.getSelectionModel().select(mPreference.getPatchGroupStreamingOption());
|
||||
mPatchGroupStreamingOptionComboBox.getSelectionModel().selectedItemProperty()
|
||||
.addListener((observable, oldValue, newValue) -> mPreference.setPatchGroupStreamingOption(newValue));
|
||||
}
|
||||
|
||||
return mPatchGroupStreamingOptionComboBox;
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import io.github.dsheirer.preference.application.ApplicationPreference;
|
||||
import io.github.dsheirer.preference.calibration.VectorCalibrationPreference;
|
||||
import io.github.dsheirer.preference.decoder.JmbeLibraryPreference;
|
||||
import io.github.dsheirer.preference.directory.DirectoryPreference;
|
||||
import io.github.dsheirer.preference.duplicate.DuplicateCallDetectionPreference;
|
||||
import io.github.dsheirer.preference.duplicate.CallManagementPreference;
|
||||
import io.github.dsheirer.preference.event.DecodeEventPreference;
|
||||
import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference;
|
||||
import io.github.dsheirer.preference.javafx.JavaFxPreferences;
|
||||
@@ -60,7 +60,7 @@ public class UserPreferences implements Listener<PreferenceType>
|
||||
private ChannelMultiFrequencyPreference mChannelMultiFrequencyPreference;
|
||||
private DecodeEventPreference mDecodeEventPreference;
|
||||
private DirectoryPreference mDirectoryPreference;
|
||||
private DuplicateCallDetectionPreference mDuplicateCallDetectionPreference;
|
||||
private CallManagementPreference mDuplicateCallDetectionPreference;
|
||||
private JmbeLibraryPreference mJmbeLibraryPreference;
|
||||
private MP3Preference mMP3Preference;
|
||||
private PlaybackPreference mPlaybackPreference;
|
||||
@@ -208,7 +208,7 @@ public class UserPreferences implements Listener<PreferenceType>
|
||||
/**
|
||||
* Duplicate call detection preferences
|
||||
*/
|
||||
public DuplicateCallDetectionPreference getDuplicateCallDetectionPreference()
|
||||
public CallManagementPreference getDuplicateCallDetectionPreference()
|
||||
{
|
||||
return mDuplicateCallDetectionPreference;
|
||||
}
|
||||
@@ -222,7 +222,7 @@ public class UserPreferences implements Listener<PreferenceType>
|
||||
mChannelMultiFrequencyPreference = new ChannelMultiFrequencyPreference(this::receive);
|
||||
mDecodeEventPreference = new DecodeEventPreference(this::receive);
|
||||
mDirectoryPreference = new DirectoryPreference(this::receive);
|
||||
mDuplicateCallDetectionPreference = new DuplicateCallDetectionPreference(this::receive);
|
||||
mDuplicateCallDetectionPreference = new CallManagementPreference(this::receive);
|
||||
mJmbeLibraryPreference = new JmbeLibraryPreference(this::receive);
|
||||
mMP3Preference = new MP3Preference(this::receive);
|
||||
mPlaybackPreference = new PlaybackPreference(this::receive);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -19,38 +19,40 @@
|
||||
|
||||
package io.github.dsheirer.preference.duplicate;
|
||||
|
||||
import io.github.dsheirer.audio.broadcast.PatchGroupStreamingOption;
|
||||
import io.github.dsheirer.preference.Preference;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import java.util.prefs.Preferences;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
/**
|
||||
* User preferences for duplicate call detection
|
||||
* User preferences for call management
|
||||
*/
|
||||
public class DuplicateCallDetectionPreference extends Preference
|
||||
public class CallManagementPreference extends Preference
|
||||
{
|
||||
private static final String PREFERENCE_KEY_DETECT_DUPLICATE_TALKGROUP = "duplicate.call.detect.talkgroup";
|
||||
private static final String PREFERENCE_KEY_DETECT_DUPLICATE_RADIO = "duplicate.call.detect.radio";
|
||||
private static final String PREFERENCE_KEY_SUPPRESS_DUPLICATE_PLAYBACK = "suppress.duplicate.audio.playback";
|
||||
private static final String PREFERENCE_KEY_SUPPRESS_DUPLICATE_RECORDING = "suppress.duplicate.audio.recording";
|
||||
private static final String PREFERENCE_KEY_SUPPRESS_DUPLICATE_STREAMING = "suppress.duplicate.audio.streaming";
|
||||
private static final String PREFERENCE_KEY_PATCHGROUP_STREAMING = "patchgroup.streaming";
|
||||
|
||||
private final static Logger mLog = LoggerFactory.getLogger(DuplicateCallDetectionPreference.class);
|
||||
private Preferences mPreferences = Preferences.userNodeForPackage(DuplicateCallDetectionPreference.class);
|
||||
private final static Logger mLog = LoggerFactory.getLogger(CallManagementPreference.class);
|
||||
private Preferences mPreferences = Preferences.userNodeForPackage(CallManagementPreference.class);
|
||||
private Boolean mDuplicateCallDetectionByTalkgroupEnabled;
|
||||
private Boolean mDuplicateCallDetectionByRadioEnabled;
|
||||
private Boolean mDuplicatePlaybackSuppressionEnabled;
|
||||
private Boolean mDuplicateRecordingSuppressionEnabled;
|
||||
private Boolean mDuplicateStreamingSuppressionEnabled;
|
||||
private PatchGroupStreamingOption mPatchGroupStreamingOption;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param updateListener to receive notifications that a preference has been updated
|
||||
*/
|
||||
public DuplicateCallDetectionPreference(Listener<PreferenceType> updateListener)
|
||||
public CallManagementPreference(Listener<PreferenceType> updateListener)
|
||||
{
|
||||
super(updateListener);
|
||||
}
|
||||
@@ -182,6 +184,51 @@ public class DuplicateCallDetectionPreference extends Preference
|
||||
{
|
||||
mPreferences.putBoolean(PREFERENCE_KEY_SUPPRESS_DUPLICATE_STREAMING, enabled);
|
||||
mDuplicateStreamingSuppressionEnabled = enabled;
|
||||
notifyPreferenceUpdated();;
|
||||
notifyPreferenceUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch group streaming option preference.
|
||||
* @return option.
|
||||
*/
|
||||
public PatchGroupStreamingOption getPatchGroupStreamingOption()
|
||||
{
|
||||
if(mPatchGroupStreamingOption == null)
|
||||
{
|
||||
String option = mPreferences.get(PREFERENCE_KEY_PATCHGROUP_STREAMING, PatchGroupStreamingOption.PATCH_GROUP.name());
|
||||
|
||||
if(option != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
mPatchGroupStreamingOption = PatchGroupStreamingOption.valueOf(option);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//Ignore ... we'll use the default value.
|
||||
}
|
||||
}
|
||||
|
||||
if(mPatchGroupStreamingOption == null)
|
||||
{
|
||||
mPatchGroupStreamingOption = PatchGroupStreamingOption.PATCH_GROUP;
|
||||
}
|
||||
}
|
||||
|
||||
return mPatchGroupStreamingOption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the patch group streaming option preference.
|
||||
* @param patchGroupStreamingOption preference
|
||||
*/
|
||||
public void setPatchGroupStreamingOption(PatchGroupStreamingOption patchGroupStreamingOption)
|
||||
{
|
||||
if(patchGroupStreamingOption != null)
|
||||
{
|
||||
mPreferences.put(PREFERENCE_KEY_PATCHGROUP_STREAMING, patchGroupStreamingOption.name());
|
||||
mPatchGroupStreamingOption = patchGroupStreamingOption;
|
||||
notifyPreferenceUpdated();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -24,6 +24,7 @@ import io.github.dsheirer.audio.AudioSegment;
|
||||
import io.github.dsheirer.audio.convert.InputAudioFormat;
|
||||
import io.github.dsheirer.audio.convert.MP3AudioConverter;
|
||||
import io.github.dsheirer.audio.convert.MP3Setting;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.record.wave.AudioMetadata;
|
||||
import io.github.dsheirer.record.wave.AudioMetadataUtils;
|
||||
@@ -58,14 +59,28 @@ public class AudioSegmentRecorder
|
||||
*/
|
||||
public static void record(AudioSegment audioSegment, Path path, RecordFormat recordFormat,
|
||||
UserPreferences userPreferences) throws IOException
|
||||
{
|
||||
record(audioSegment, path, recordFormat, userPreferences, audioSegment.getIdentifierCollection());
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the audio segment to the specified path using the specified recording format
|
||||
* @param audioSegment to record
|
||||
* @param path for the recording
|
||||
* @param recordFormat to use (WAVE, MP3)
|
||||
* @param identifierCollection to use instead of the audioSegment's embedded collection.
|
||||
* @throws IOException on any errors
|
||||
*/
|
||||
public static void record(AudioSegment audioSegment, Path path, RecordFormat recordFormat,
|
||||
UserPreferences userPreferences, IdentifierCollection identifierCollection) throws IOException
|
||||
{
|
||||
switch(recordFormat)
|
||||
{
|
||||
case MP3:
|
||||
recordMP3(audioSegment, path, userPreferences);
|
||||
recordMP3(audioSegment, path, userPreferences, identifierCollection);
|
||||
break;
|
||||
case WAVE:
|
||||
recordWAVE(audioSegment, path);
|
||||
recordWAVE(audioSegment, path, identifierCollection);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized recording format [" + recordFormat.name() + "]");
|
||||
@@ -76,16 +91,19 @@ public class AudioSegmentRecorder
|
||||
* Records the audio segment as an MP3 file to the specified path.
|
||||
* @param audioSegment to record
|
||||
* @param path for the recording
|
||||
* @param userPreferences for configuration
|
||||
* @param identifierCollection to use instead of the collection embedded in the audio segment
|
||||
* @throws IOException on any errors
|
||||
*/
|
||||
public static void recordMP3(AudioSegment audioSegment, Path path, UserPreferences userPreferences) throws IOException
|
||||
public static void recordMP3(AudioSegment audioSegment, Path path, UserPreferences userPreferences,
|
||||
IdentifierCollection identifierCollection) throws IOException
|
||||
{
|
||||
if(audioSegment.hasAudio())
|
||||
{
|
||||
OutputStream outputStream = new FileOutputStream(path.toFile());
|
||||
|
||||
//Write ID3 metadata
|
||||
Map<AudioMetadata,String> metadataMap = AudioMetadataUtils.getMetadataMap(audioSegment.getIdentifierCollection(),
|
||||
Map<AudioMetadata,String> metadataMap = AudioMetadataUtils.getMetadataMap(identifierCollection,
|
||||
audioSegment.getAliasList());
|
||||
|
||||
byte[] id3Bytes = AudioMetadataUtils.getMP3ID3(metadataMap);
|
||||
@@ -123,9 +141,10 @@ public class AudioSegmentRecorder
|
||||
* Records the audio segment as a WAVe file to the specified path.
|
||||
* @param audioSegment to record
|
||||
* @param path for the recording
|
||||
* @param identifierCollection to use instead of the audioSegment's embedded identifier collection
|
||||
* @throws IOException on any errors
|
||||
*/
|
||||
public static void recordWAVE(AudioSegment audioSegment, Path path) throws IOException
|
||||
public static void recordWAVE(AudioSegment audioSegment, Path path, IdentifierCollection identifierCollection) throws IOException
|
||||
{
|
||||
if(audioSegment.hasAudio())
|
||||
{
|
||||
@@ -136,7 +155,7 @@ public class AudioSegmentRecorder
|
||||
writer.writeData(ConversionUtils.convertToSigned16BitSamples(audioBuffer));
|
||||
}
|
||||
|
||||
Map<AudioMetadata,String> metadataMap = AudioMetadataUtils.getMetadataMap(audioSegment.getIdentifierCollection(),
|
||||
Map<AudioMetadata,String> metadataMap = AudioMetadataUtils.getMetadataMap(identifierCollection,
|
||||
audioSegment.getAliasList());
|
||||
|
||||
ByteBuffer listChunk = AudioMetadataUtils.getLISTChunk(metadataMap);
|
||||
|
||||
Reference in New Issue
Block a user