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);
|
InputAudioComboBox.Items.Add(sourceDevices[i].Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set output device & add to inputs too
|
||||||
for (int i = 0; i < targetDevices.Count; i++)
|
for (int i = 0; i < targetDevices.Count; i++)
|
||||||
{
|
{
|
||||||
if(targetDevices[i].Id == defaultOutput.Id)
|
if(targetDevices[i].Id == defaultOutput.Id)
|
||||||
@ -211,7 +213,6 @@ namespace Audio_Router
|
|||||||
};
|
};
|
||||||
result = await AudioGraph.CreateAsync(settings);
|
result = await AudioGraph.CreateAsync(settings);
|
||||||
ag = result.Graph;
|
ag = result.Graph;
|
||||||
|
|
||||||
if (result.Status != AudioGraphCreationStatus.Success)
|
if (result.Status != AudioGraphCreationStatus.Success)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -25,10 +25,11 @@ namespace Audio_Router_WPF
|
|||||||
|
|
||||||
Dispatcher uiDispatcher { get; set; }
|
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)
|
public AudioGraphConnection(AudioGraph graph, AudioDeviceOutputNode targetDevice, AudioDeviceInputNode sourceDevice, Dispatcher uiDispatcher)
|
||||||
{
|
{
|
||||||
Graph = graph;
|
Graph = graph;
|
||||||
@ -128,23 +129,43 @@ namespace Audio_Router_WPF
|
|||||||
private void Graph_QuantumProcessed(AudioGraph sender, object args)
|
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
|
// 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;
|
if (latencyLabel is null) return;
|
||||||
|
string result = (Graph.LatencyInSamples / (double)Graph.SamplesPerQuantum * SamplesPerMS).ToString("0.#") + " ms";
|
||||||
|
|
||||||
uiDispatcher.Invoke(() =>
|
uiDispatcher.Invoke(() =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Try to set the label, otherwise it may have already been destroyed
|
||||||
latencyLabel.Text = result;
|
latencyLabel.Text = result;
|
||||||
}
|
}
|
||||||
catch
|
catch (NullReferenceException)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}, DispatcherPriority.Background);
|
}, 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)
|
private void VolumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
|
||||||
@ -153,10 +174,12 @@ namespace Audio_Router_WPF
|
|||||||
SourceDevice.OutgoingGain = e.NewValue;
|
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)
|
private void DestroyButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
latencyLabel = null;
|
|
||||||
Graph.QuantumProcessed -= Graph_QuantumProcessed;
|
Graph.QuantumProcessed -= Graph_QuantumProcessed;
|
||||||
|
Graph.ResetAllNodes();
|
||||||
|
latencyLabel = null;
|
||||||
Graph.Stop();
|
Graph.Stop();
|
||||||
TargetDevice.Dispose();
|
TargetDevice.Dispose();
|
||||||
SourceDevice.Dispose();
|
SourceDevice.Dispose();
|
||||||
@ -172,6 +195,7 @@ namespace Audio_Router_WPF
|
|||||||
public void OnHide()
|
public void OnHide()
|
||||||
{
|
{
|
||||||
latencyLabel = null;
|
latencyLabel = null;
|
||||||
|
Graph.QuantumProcessed -= Graph_QuantumProcessed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:Audio_Router_WPF"
|
xmlns:local="clr-namespace:Audio_Router_WPF"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="MainWindow" Height="450" Width="800">
|
Title="Audio Router WPF" Height="450" Width="800">
|
||||||
<Grid VerticalAlignment="Stretch">
|
<Grid VerticalAlignment="Stretch">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
@ -18,10 +18,10 @@
|
|||||||
<TextBlock Text="Input Audio Device:" TextWrapping="Wrap" Margin="0,10,0,0" Foreground="White" HorizontalAlignment="Center"/>
|
<TextBlock Text="Input Audio Device:" TextWrapping="Wrap" Margin="0,10,0,0" Foreground="White" HorizontalAlignment="Center"/>
|
||||||
<ComboBox x:Name="InputAudioComboBox" MinWidth="500" 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"/>
|
<TextBlock Text="Output Audio Device:" TextWrapping="Wrap" Margin="0,10,0,0" Foreground="White" HorizontalAlignment="Center"/>
|
||||||
<ComboBox x:Name="OutputAudioComboBox" MinWidth="500" HorizontalAlignment="Center"/>
|
<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"/>
|
<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">
|
<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>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ScrollViewer Grid.Row="1" Margin="10,5,10,5" Padding="0,0,10,0">
|
<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
|
// The list of audio devices
|
||||||
DeviceInformationCollection sourceDevices;
|
DeviceInformationCollection sourceDevices;
|
||||||
DeviceInformationCollection targetDevices;
|
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()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
}
|
||||||
|
protected override void OnInitialized(EventArgs e)
|
||||||
|
{
|
||||||
|
base.OnInitialized(e);
|
||||||
_ = Task.Run(() => FindAudioSources());
|
_ = Task.Run(() => FindAudioSources());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void AddAudioGraphButton_Click(object sender, RoutedEventArgs e)
|
private async void AddAudioGraphButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
await CreateAudioGraph();
|
await CreateAudioGraph();
|
||||||
@ -57,6 +63,7 @@ namespace Audio_Router_WPF
|
|||||||
DeviceInformation defaultOutput = await DeviceInformation.CreateFromIdAsync(MediaDevice.GetDefaultAudioRenderId(AudioDeviceRole.Default));
|
DeviceInformation defaultOutput = await DeviceInformation.CreateFromIdAsync(MediaDevice.GetDefaultAudioRenderId(AudioDeviceRole.Default));
|
||||||
DeviceInformation defaultInput = await DeviceInformation.CreateFromIdAsync(MediaDevice.GetDefaultAudioCaptureId(AudioDeviceRole.Default));
|
DeviceInformation defaultInput = await DeviceInformation.CreateFromIdAsync(MediaDevice.GetDefaultAudioCaptureId(AudioDeviceRole.Default));
|
||||||
|
|
||||||
|
|
||||||
Dispatcher.Invoke(() =>
|
Dispatcher.Invoke(() =>
|
||||||
{
|
{
|
||||||
// Set the Input device
|
// Set the Input device
|
||||||
@ -72,16 +79,19 @@ namespace Audio_Router_WPF
|
|||||||
_ = InputAudioComboBox.Items.Add(sourceDevices[i].Name);
|
_ = 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++)
|
for (int i = 0; i < targetDevices.Count; i++)
|
||||||
{
|
{
|
||||||
if (targetDevices[i].Id == defaultOutput.Id)
|
if (targetDevices[i].Id == defaultOutput.Id)
|
||||||
{
|
{
|
||||||
_ = OutputAudioComboBox.Items.Add(targetDevices[i].Name + " (Default Device)");
|
_ = OutputAudioComboBox.Items.Add(targetDevices[i].Name + " (Default Device)");
|
||||||
|
//_ = InputAudioComboBox.Items.Add(targetDevices[i].Name + " (Default Device)");
|
||||||
OutputAudioComboBox.SelectedIndex = i;
|
OutputAudioComboBox.SelectedIndex = i;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_ = OutputAudioComboBox.Items.Add(targetDevices[i].Name);
|
_ = OutputAudioComboBox.Items.Add(targetDevices[i].Name);
|
||||||
|
//_ = InputAudioComboBox.Items.Add(targetDevices[i].Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -91,8 +101,21 @@ namespace Audio_Router_WPF
|
|||||||
private async Task CreateAudioGraph()
|
private async Task CreateAudioGraph()
|
||||||
{
|
{
|
||||||
App.displayRequest.RequestActive();
|
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 outputDevice = targetDevices[OutputAudioComboBox.SelectedIndex];
|
||||||
DeviceInformation inputDevice = sourceDevices[InputAudioComboBox.SelectedIndex];
|
|
||||||
AudioGraphSettings settings = LowLatencyCheckbox.IsChecked == true
|
AudioGraphSettings settings = LowLatencyCheckbox.IsChecked == true
|
||||||
? new AudioGraphSettings(AudioRenderCategory.Media)
|
? new AudioGraphSettings(AudioRenderCategory.Media)
|
||||||
{
|
{
|
||||||
@ -121,8 +144,9 @@ namespace Audio_Router_WPF
|
|||||||
return;
|
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)
|
if (deviceInputNodeResult.Status != AudioDeviceNodeCreationStatus.Success)
|
||||||
{
|
{
|
||||||
// Fail-safe, switch to using the default encoding properties
|
// Fail-safe, switch to using the default encoding properties
|
||||||
@ -143,9 +167,9 @@ namespace Audio_Router_WPF
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the device output node connection
|
// Create the device output node connection
|
||||||
CreateAudioDeviceOutputNodeResult deviceOutputNodeResult = await ag.CreateDeviceOutputNodeAsync();
|
CreateAudioDeviceOutputNodeResult deviceOutputNodeResult = await ag.CreateDeviceOutputNodeAsync();
|
||||||
|
//deviceOutputNodeResult.DeviceOutputNode.Creat
|
||||||
if (deviceOutputNodeResult.Status != AudioDeviceNodeCreationStatus.Success)
|
if (deviceOutputNodeResult.Status != AudioDeviceNodeCreationStatus.Success)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -155,7 +179,6 @@ namespace Audio_Router_WPF
|
|||||||
ag.Start();
|
ag.Start();
|
||||||
AudioGraphConnection graphConnection = new AudioGraphConnection(ag, deviceOutputNodeResult.DeviceOutputNode, deviceInputNodeResult.DeviceInputNode, Dispatcher);
|
AudioGraphConnection graphConnection = new AudioGraphConnection(ag, deviceOutputNodeResult.DeviceOutputNode, deviceInputNodeResult.DeviceInputNode, Dispatcher);
|
||||||
App.appRef.audioGraphConnections.Add(graphConnection);
|
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
|
// 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);
|
Grid renderGrid = graphConnection.GetAsDisplayItem(out Button visualButton);
|
||||||
CreatedGraphs.Children.Add(renderGrid);
|
CreatedGraphs.Children.Add(renderGrid);
|
||||||
@ -165,6 +188,7 @@ namespace Audio_Router_WPF
|
|||||||
visualButton.Click -= handler;
|
visualButton.Click -= handler;
|
||||||
DestroyVisualElement(ref renderGrid);
|
DestroyVisualElement(ref renderGrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
visualButton.Click += handler;
|
visualButton.Click += handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user