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:
Denny Sheirer
2023-12-17 06:58:09 -05:00
committed by GitHub
11 changed files with 292 additions and 68 deletions

View File

@@ -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);

View File

@@ -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
*/

View File

@@ -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;
}
}

View File

@@ -125,7 +125,7 @@ public class MBECallSequenceConverter
try
{
AudioSegmentRecorder.recordWAVE(audioSegment, outputPath);
AudioSegmentRecorder.recordWAVE(audioSegment, outputPath, audioSegment.getIdentifierCollection());
}
catch(IOException ioe)
{

View File

@@ -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:

View File

@@ -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"),

View File

@@ -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));

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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();
}
}
}

View File

@@ -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);