#1506 Resolves multiple performance issues: tuner freezing after a period of run time, buffer overflows, degraded audio playback. (#1513)

-Reduces message and event histories per channel from 500 to 200.
-Resolves issue of tuner freezing due to cancelled USB transfer buffers not being resubmitted.
-Reworks the audio playback system to remove in-progress call preemption and remove thread locks which can cause glitchy behavior during audio playback.
-Updates audio outputs to also use single thread pool for processing audio segments.
-Updates MBECallSequenceConverter to batch convert MBE to WAV audio.
-Unifies recording filename prefixes across baseband, mbe and binary recordings.
-Resolves issue with registering heartbeat manager to dispatcher.
-Uses single-thread thread pools for Dispatcher to support batch & process to overcome context switching consistency issue in Windows.

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2023-04-11 04:07:41 -04:00
committed by GitHub
parent 32a8363b45
commit 372d2ff870
34 changed files with 802 additions and 1112 deletions

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,10 +24,9 @@ import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.playback.PlaybackPreference;
import io.github.dsheirer.source.mixer.MixerChannelConfiguration;
import io.github.dsheirer.source.mixer.MixerManager;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
@@ -49,9 +48,6 @@ import org.slf4j.LoggerFactory;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
/**
@@ -60,20 +56,16 @@ import java.nio.ShortBuffer;
public class PlaybackPreferenceEditor extends HBox
{
private final static Logger mLog = LoggerFactory.getLogger(PlaybackPreferenceEditor.class);
private PlaybackPreference mPlaybackPreference;
private final PlaybackPreference mPlaybackPreference;
private GridPane mEditorPane;
private ComboBox<MixerChannelConfiguration> mMixerComboBox;
private Button mMixerTestButton;
private ToggleSwitch mUseAudioSegmentStartToneSwitch;
private Button mTestStartToneButton;
private ToggleSwitch mUseAudioSegmentPreemptToneSwitch;
private Button mTestPreemptToneButton;
private ToggleSwitch mUseAudioSegmentDropToneSwitch;
private Button mTestDropToneButton;
private ComboBox<ToneFrequency> mStartToneFrequencyComboBox;
private ComboBox<ToneVolume> mStartToneVolumeComboBox;
private ComboBox<ToneFrequency> mPreemptToneFrequencyComboBox;
private ComboBox<ToneVolume> mPreemptToneVolumeComboBox;
private ComboBox<ToneFrequency> mDropToneFrequencyComboBox;
private ComboBox<ToneVolume> mDropToneVolumeComboBox;
@@ -114,18 +106,6 @@ public class PlaybackPreferenceEditor extends HBox
mEditorPane.add(getStartToneVolumeComboBox(), 4, row);
mEditorPane.add(getTestStartToneButton(), 5, row);
mEditorPane.add(getUseAudioSegmentPreemptToneSwitch(), 0, ++row);
mEditorPane.add(new Label("Priority Preempt Tone"), 1, row, 3, 1);
Label preemptFrequencyLabel = new Label("Frequency:");
GridPane.setHalignment(preemptFrequencyLabel, HPos.RIGHT);
mEditorPane.add(preemptFrequencyLabel, 1, ++row);
mEditorPane.add(getPreemptToneFrequencyComboBox(), 2, row);
Label preemptVolumeLabel = new Label("Volume:");
GridPane.setHalignment(preemptVolumeLabel, HPos.RIGHT);
mEditorPane.add(preemptVolumeLabel, 3, row);
mEditorPane.add(getPreemptToneVolumeComboBox(), 4, row);
mEditorPane.add(getTestPreemptToneButton(), 5, row);
mEditorPane.add(getUseAudioSegmentDropToneSwitch(), 0, ++row);
mEditorPane.add(new Label("Drop Tone - Do Not Monitor"), 1, row, 3, 1);
Label dropFrequencyLabel = new Label("Frequency:");
@@ -177,52 +157,21 @@ public class PlaybackPreferenceEditor extends HBox
mUseAudioSegmentStartToneSwitch = new ToggleSwitch();
mUseAudioSegmentStartToneSwitch.setAlignment(Pos.BASELINE_RIGHT);
mUseAudioSegmentStartToneSwitch.setSelected(mPlaybackPreference.getUseAudioSegmentStartTone());
mUseAudioSegmentStartToneSwitch.selectedProperty().addListener(new ChangeListener<Boolean>()
{
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue)
{
mPlaybackPreference.setUseAudioSegmentStartTone(newValue);
}
});
mUseAudioSegmentStartToneSwitch.selectedProperty().addListener((observable, oldValue, newValue) ->
mPlaybackPreference.setUseAudioSegmentStartTone(newValue));
}
return mUseAudioSegmentStartToneSwitch;
}
private ToggleSwitch getUseAudioSegmentPreemptToneSwitch()
{
if(mUseAudioSegmentPreemptToneSwitch == null)
{
mUseAudioSegmentPreemptToneSwitch = new ToggleSwitch();
mUseAudioSegmentPreemptToneSwitch.setSelected(mPlaybackPreference.getUseAudioSegmentPreemptTone());
mUseAudioSegmentPreemptToneSwitch.selectedProperty().addListener(new ChangeListener<Boolean>()
{
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue)
{
mPlaybackPreference.setUseAudioSegmentPreemptTone(newValue);
}
});
}
return mUseAudioSegmentPreemptToneSwitch;
}
private ToggleSwitch getUseAudioSegmentDropToneSwitch()
{
if(mUseAudioSegmentDropToneSwitch == null)
{
mUseAudioSegmentDropToneSwitch = new ToggleSwitch();
mUseAudioSegmentDropToneSwitch.setSelected(mPlaybackPreference.getUseAudioSegmentDropTone());
mUseAudioSegmentDropToneSwitch.selectedProperty().addListener(new ChangeListener<Boolean>()
{
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue)
{
mPlaybackPreference.setUseAudioSegmentDropTone(newValue);
}
});
mUseAudioSegmentDropToneSwitch.selectedProperty().addListener((observable, oldValue, newValue) ->
mPlaybackPreference.setUseAudioSegmentDropTone(newValue));
}
return mUseAudioSegmentDropToneSwitch;
@@ -236,14 +185,7 @@ public class PlaybackPreferenceEditor extends HBox
IconNode iconNode = new IconNode(FontAwesome.PLAY);
iconNode.setFill(Color.CORNFLOWERBLUE);
mTestStartToneButton.setGraphic(iconNode);
mTestStartToneButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
play(mPlaybackPreference.getStartTone());
}
});
mTestStartToneButton.setOnAction(event -> play(mPlaybackPreference.getStartTone()));
mTestStartToneButton.disableProperty().bind(getUseAudioSegmentStartToneSwitch().selectedProperty().not());
}
@@ -251,29 +193,6 @@ public class PlaybackPreferenceEditor extends HBox
return mTestStartToneButton;
}
public Button getTestPreemptToneButton()
{
if(mTestPreemptToneButton == null)
{
mTestPreemptToneButton = new Button("Test");
IconNode iconNode = new IconNode(FontAwesome.PLAY);
iconNode.setFill(Color.CORNFLOWERBLUE);
mTestPreemptToneButton.setGraphic(iconNode);
mTestPreemptToneButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
play(mPlaybackPreference.getPreemptTone());
}
});
mTestPreemptToneButton.disableProperty().bind(getUseAudioSegmentPreemptToneSwitch().selectedProperty().not());
}
return mTestPreemptToneButton;
}
public Button getTestDropToneButton()
{
if(mTestDropToneButton == null)
@@ -282,14 +201,7 @@ public class PlaybackPreferenceEditor extends HBox
IconNode iconNode = new IconNode(FontAwesome.PLAY);
iconNode.setFill(Color.CORNFLOWERBLUE);
mTestDropToneButton.setGraphic(iconNode);
mTestDropToneButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
play(mPlaybackPreference.getDropTone());
}
});
mTestDropToneButton.setOnAction(event -> play(mPlaybackPreference.getDropTone()));
mTestDropToneButton.disableProperty().bind(getUseAudioSegmentDropToneSwitch().selectedProperty().not());
}
@@ -357,36 +269,6 @@ public class PlaybackPreferenceEditor extends HBox
return mStartToneVolumeComboBox;
}
public ComboBox<ToneFrequency> getPreemptToneFrequencyComboBox()
{
if(mPreemptToneFrequencyComboBox == null)
{
mPreemptToneFrequencyComboBox = new ComboBox<>();
mPreemptToneFrequencyComboBox.getItems().addAll(ToneFrequency.values());
mPreemptToneFrequencyComboBox.getSelectionModel().select(mPlaybackPreference.getPreemptToneFrequency());
mPreemptToneFrequencyComboBox.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> mPlaybackPreference.setPreemptToneFrequency(newValue));
mPreemptToneFrequencyComboBox.disableProperty().bind(getUseAudioSegmentPreemptToneSwitch().selectedProperty().not());
}
return mPreemptToneFrequencyComboBox;
}
public ComboBox<ToneVolume> getPreemptToneVolumeComboBox()
{
if(mPreemptToneVolumeComboBox == null)
{
mPreemptToneVolumeComboBox = new ComboBox<>();
mPreemptToneVolumeComboBox.getItems().addAll(ToneVolume.values());
mPreemptToneVolumeComboBox.getSelectionModel().select(mPlaybackPreference.getPreemptToneVolume());
mPreemptToneVolumeComboBox.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> mPlaybackPreference.setPreemptToneVolume(newValue));
mPreemptToneVolumeComboBox.disableProperty().bind(getUseAudioSegmentPreemptToneSwitch().selectedProperty().not());
}
return mPreemptToneVolumeComboBox;
}
/**
* Plays the audio buffer over the default mono playback device
* @param audioSamples with 8 kHz mono PCM samples