Fixed a memory leak in the WPF project
This commit is contained in:
parent
09a2398967
commit
8d81ff91aa
@ -147,6 +147,8 @@ namespace Audio_Router
|
||||
InputAudioComboBox.Items.Add(sourceDevices[i].Name);
|
||||
}
|
||||
}
|
||||
|
||||
// Set output device & add to inputs too
|
||||
for (int i = 0; i < targetDevices.Count; i++)
|
||||
{
|
||||
if(targetDevices[i].Id == defaultOutput.Id)
|
||||
@ -211,7 +213,6 @@ namespace Audio_Router
|
||||
};
|
||||
result = await AudioGraph.CreateAsync(settings);
|
||||
ag = result.Graph;
|
||||
|
||||
if (result.Status != AudioGraphCreationStatus.Success)
|
||||
{
|
||||
return;
|
||||
|
@ -25,10 +25,11 @@ namespace Audio_Router_WPF
|
||||
|
||||
Dispatcher uiDispatcher { get; set; }
|
||||
|
||||
TextBlock latencyLabel { get; set; }
|
||||
TextBlock latencyLabel { get; set; } = new TextBlock();
|
||||
|
||||
int quantaCount = 0;
|
||||
int quantaCount;
|
||||
|
||||
#pragma warning disable CS8618 // Cannot convert null literal to non-nullable reference type.
|
||||
public AudioGraphConnection(AudioGraph graph, AudioDeviceOutputNode targetDevice, AudioDeviceInputNode sourceDevice, Dispatcher uiDispatcher)
|
||||
{
|
||||
Graph = graph;
|
||||
@ -128,23 +129,43 @@ namespace Audio_Router_WPF
|
||||
private void Graph_QuantumProcessed(AudioGraph sender, object args)
|
||||
{
|
||||
// updates aren't urgent, only process after some time (this is 1s, with the Windows default of 10 ms / quanta
|
||||
if (quantaCount++ == 100)
|
||||
if (quantaCount % 100 == 0)
|
||||
{
|
||||
string result = (Graph.LatencyInSamples / (double)Graph.SamplesPerQuantum * SamplesPerMS).ToString("0.#") + " ms";
|
||||
if (latencyLabel is null) return;
|
||||
string result = (Graph.LatencyInSamples / (double)Graph.SamplesPerQuantum * SamplesPerMS).ToString("0.#") + " ms";
|
||||
|
||||
uiDispatcher.Invoke(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to set the label, otherwise it may have already been destroyed
|
||||
latencyLabel.Text = result;
|
||||
}
|
||||
catch
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
|
||||
}
|
||||
}, DispatcherPriority.Background);
|
||||
quantaCount = 0;
|
||||
result = null;
|
||||
}
|
||||
/// ----------------------
|
||||
/// || About this: ||
|
||||
/// ----------------------
|
||||
/// Running AudioGraph in WPF causes what seems to be the captured audio data to
|
||||
/// never go out of scope (this does not happen in UWP, as shown in the near-identical code from the
|
||||
/// UWP project), so the memory allocations for the project keep increasing.
|
||||
/// Stopping the AudioGraph causes these allocations to finalise, so frees up the space.
|
||||
/// we can call ResetAllNodes() periodically (i.e.: every 10s (1000 * 10 ms)) to reset the
|
||||
/// nodes
|
||||
///
|
||||
if (quantaCount == 1000)
|
||||
{
|
||||
//SourceDevice.Reset();
|
||||
Graph.ResetAllNodes();
|
||||
// GC.Collect(); // We can let the GC handle itself
|
||||
}
|
||||
quantaCount++;
|
||||
quantaCount %= 1001;
|
||||
}
|
||||
|
||||
private void VolumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
|
||||
@ -153,10 +174,12 @@ namespace Audio_Router_WPF
|
||||
SourceDevice.OutgoingGain = e.NewValue;
|
||||
}
|
||||
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
private void DestroyButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
latencyLabel = null;
|
||||
Graph.QuantumProcessed -= Graph_QuantumProcessed;
|
||||
Graph.ResetAllNodes();
|
||||
latencyLabel = null;
|
||||
Graph.Stop();
|
||||
TargetDevice.Dispose();
|
||||
SourceDevice.Dispose();
|
||||
@ -172,6 +195,7 @@ namespace Audio_Router_WPF
|
||||
public void OnHide()
|
||||
{
|
||||
latencyLabel = null;
|
||||
Graph.QuantumProcessed -= Graph_QuantumProcessed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Audio_Router_WPF"
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Height="450" Width="800">
|
||||
Title="Audio Router WPF" Height="450" Width="800">
|
||||
<Grid VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
@ -18,10 +18,10 @@
|
||||
<TextBlock Text="Input Audio Device:" TextWrapping="Wrap" Margin="0,10,0,0" Foreground="White" HorizontalAlignment="Center"/>
|
||||
<ComboBox x:Name="InputAudioComboBox" MinWidth="500" HorizontalAlignment="Center"/>
|
||||
<TextBlock Text="Output Audio Device:" TextWrapping="Wrap" Margin="0,10,0,0" Foreground="White" HorizontalAlignment="Center"/>
|
||||
<ComboBox x:Name="OutputAudioComboBox" MinWidth="500" HorizontalAlignment="Center"/>
|
||||
<CheckBox x:Name="LowLatencyCheckbox" Content="Low Latency Mode (Reduces audio quality)" HorizontalAlignment="Center" Margin="0,10,0,0"/>
|
||||
<ComboBox x:Name="OutputAudioComboBox" MinWidth="500" HorizontalAlignment="Center" Background="Black" BorderBrush="Black"/>
|
||||
<CheckBox x:Name="LowLatencyCheckbox" Content="Low Latency Mode (Reduces audio quality)" HorizontalAlignment="Center" Margin="0,10,0,0" Foreground="White"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,20,0,5">
|
||||
<Button x:Name="AddAudioGraphButton" Content="Add Audio Graph" HorizontalContentAlignment="Center" Background="#54FFFFFF" Click="AddAudioGraphButton_Click"/>
|
||||
<Button x:Name="AddAudioGraphButton" Content="Add Audio Graph" HorizontalContentAlignment="Center" Background="#54FFFFFF" Click="AddAudioGraphButton_Click" Foreground="White"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<ScrollViewer Grid.Row="1" Margin="10,5,10,5" Padding="0,0,10,0">
|
||||
|
@ -31,12 +31,18 @@ namespace Audio_Router_WPF
|
||||
// The list of audio devices
|
||||
DeviceInformationCollection sourceDevices;
|
||||
DeviceInformationCollection targetDevices;
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. (these fields cannot be assigned
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
}
|
||||
protected override void OnInitialized(EventArgs e)
|
||||
{
|
||||
base.OnInitialized(e);
|
||||
_ = Task.Run(() => FindAudioSources());
|
||||
}
|
||||
|
||||
private async void AddAudioGraphButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await CreateAudioGraph();
|
||||
@ -57,6 +63,7 @@ namespace Audio_Router_WPF
|
||||
DeviceInformation defaultOutput = await DeviceInformation.CreateFromIdAsync(MediaDevice.GetDefaultAudioRenderId(AudioDeviceRole.Default));
|
||||
DeviceInformation defaultInput = await DeviceInformation.CreateFromIdAsync(MediaDevice.GetDefaultAudioCaptureId(AudioDeviceRole.Default));
|
||||
|
||||
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
// Set the Input device
|
||||
@ -72,16 +79,19 @@ namespace Audio_Router_WPF
|
||||
_ = InputAudioComboBox.Items.Add(sourceDevices[i].Name);
|
||||
}
|
||||
}
|
||||
// _ = InputAudioComboBox.Items.Add("--Output nodes--"); // This requires further work, implementing W
|
||||
for (int i = 0; i < targetDevices.Count; i++)
|
||||
{
|
||||
if (targetDevices[i].Id == defaultOutput.Id)
|
||||
{
|
||||
_ = OutputAudioComboBox.Items.Add(targetDevices[i].Name + " (Default Device)");
|
||||
//_ = InputAudioComboBox.Items.Add(targetDevices[i].Name + " (Default Device)");
|
||||
OutputAudioComboBox.SelectedIndex = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = OutputAudioComboBox.Items.Add(targetDevices[i].Name);
|
||||
//_ = InputAudioComboBox.Items.Add(targetDevices[i].Name);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -91,8 +101,21 @@ namespace Audio_Router_WPF
|
||||
private async Task CreateAudioGraph()
|
||||
{
|
||||
App.displayRequest.RequestActive();
|
||||
bool isInputDevice = true;
|
||||
DeviceInformation inputDevice;
|
||||
if (InputAudioComboBox.SelectedIndex < sourceDevices.Count)
|
||||
{
|
||||
isInputDevice = true;
|
||||
inputDevice = sourceDevices[InputAudioComboBox.SelectedIndex];
|
||||
}
|
||||
else if (InputAudioComboBox.SelectedIndex == sourceDevices.Count) return;
|
||||
else
|
||||
{
|
||||
isInputDevice = false;
|
||||
inputDevice = targetDevices[InputAudioComboBox.SelectedIndex - (sourceDevices.Count + 1)];
|
||||
}
|
||||
DeviceInformation outputDevice = targetDevices[OutputAudioComboBox.SelectedIndex];
|
||||
DeviceInformation inputDevice = sourceDevices[InputAudioComboBox.SelectedIndex];
|
||||
|
||||
AudioGraphSettings settings = LowLatencyCheckbox.IsChecked == true
|
||||
? new AudioGraphSettings(AudioRenderCategory.Media)
|
||||
{
|
||||
@ -121,8 +144,9 @@ namespace Audio_Router_WPF
|
||||
return;
|
||||
}
|
||||
|
||||
CreateAudioDeviceInputNodeResult deviceInputNodeResult = await ag.CreateDeviceInputNodeAsync(MediaCategory.Other, ag.EncodingProperties, inputDevice);
|
||||
|
||||
//CreateAudioDeviceInputNodeResult deviceInputNodeResult = await ag.CreateDeviceInputNodeAsync(MediaCategory.Other, ag.EncodingProperties, inputDevice);
|
||||
CreateAudioDeviceInputNodeResult deviceInputNodeResult = await ag.CreateDeviceInputNodeAsync(,,);
|
||||
AudioFrameInputNode g = ag.CreateFrameInputNode();
|
||||
if (deviceInputNodeResult.Status != AudioDeviceNodeCreationStatus.Success)
|
||||
{
|
||||
// Fail-safe, switch to using the default encoding properties
|
||||
@ -143,9 +167,9 @@ namespace Audio_Router_WPF
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the device output node connection
|
||||
CreateAudioDeviceOutputNodeResult deviceOutputNodeResult = await ag.CreateDeviceOutputNodeAsync();
|
||||
//deviceOutputNodeResult.DeviceOutputNode.Creat
|
||||
if (deviceOutputNodeResult.Status != AudioDeviceNodeCreationStatus.Success)
|
||||
{
|
||||
return;
|
||||
@ -155,7 +179,6 @@ namespace Audio_Router_WPF
|
||||
ag.Start();
|
||||
AudioGraphConnection graphConnection = new AudioGraphConnection(ag, deviceOutputNodeResult.DeviceOutputNode, deviceInputNodeResult.DeviceInputNode, Dispatcher);
|
||||
App.appRef.audioGraphConnections.Add(graphConnection);
|
||||
|
||||
// Hold a reference to the created button; when clicked we must also ensure we remove this visual element from the grid display
|
||||
Grid renderGrid = graphConnection.GetAsDisplayItem(out Button visualButton);
|
||||
CreatedGraphs.Children.Add(renderGrid);
|
||||
@ -165,6 +188,7 @@ namespace Audio_Router_WPF
|
||||
visualButton.Click -= handler;
|
||||
DestroyVisualElement(ref renderGrid);
|
||||
}
|
||||
|
||||
visualButton.Click += handler;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user