Added solution files

This commit is contained in:
Brychan Dempsey 2021-09-23 17:30:38 +12:00
parent 62d14c19cc
commit 7f65b4a7a6
26 changed files with 1173 additions and 0 deletions

View File

@ -0,0 +1,4 @@
[*.cs]
# IDE0011: Add braces
csharp_prefer_braces = false

View File

@ -0,0 +1,56 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31710.8
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Audio Router", "Audio Router\Audio Router.csproj", "{92D76120-5578-484A-BB93-24D105B48043}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AAA9AA8A-8F68-425E-B705-B650E4EBA213}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{92D76120-5578-484A-BB93-24D105B48043}.Debug|ARM.ActiveCfg = Debug|ARM
{92D76120-5578-484A-BB93-24D105B48043}.Debug|ARM.Build.0 = Debug|ARM
{92D76120-5578-484A-BB93-24D105B48043}.Debug|ARM.Deploy.0 = Debug|ARM
{92D76120-5578-484A-BB93-24D105B48043}.Debug|ARM64.ActiveCfg = Debug|ARM64
{92D76120-5578-484A-BB93-24D105B48043}.Debug|ARM64.Build.0 = Debug|ARM64
{92D76120-5578-484A-BB93-24D105B48043}.Debug|ARM64.Deploy.0 = Debug|ARM64
{92D76120-5578-484A-BB93-24D105B48043}.Debug|x64.ActiveCfg = Debug|x64
{92D76120-5578-484A-BB93-24D105B48043}.Debug|x64.Build.0 = Debug|x64
{92D76120-5578-484A-BB93-24D105B48043}.Debug|x64.Deploy.0 = Debug|x64
{92D76120-5578-484A-BB93-24D105B48043}.Debug|x86.ActiveCfg = Debug|x86
{92D76120-5578-484A-BB93-24D105B48043}.Debug|x86.Build.0 = Debug|x86
{92D76120-5578-484A-BB93-24D105B48043}.Debug|x86.Deploy.0 = Debug|x86
{92D76120-5578-484A-BB93-24D105B48043}.Release|ARM.ActiveCfg = Release|ARM
{92D76120-5578-484A-BB93-24D105B48043}.Release|ARM.Build.0 = Release|ARM
{92D76120-5578-484A-BB93-24D105B48043}.Release|ARM.Deploy.0 = Release|ARM
{92D76120-5578-484A-BB93-24D105B48043}.Release|ARM64.ActiveCfg = Release|ARM64
{92D76120-5578-484A-BB93-24D105B48043}.Release|ARM64.Build.0 = Release|ARM64
{92D76120-5578-484A-BB93-24D105B48043}.Release|ARM64.Deploy.0 = Release|ARM64
{92D76120-5578-484A-BB93-24D105B48043}.Release|x64.ActiveCfg = Release|x64
{92D76120-5578-484A-BB93-24D105B48043}.Release|x64.Build.0 = Release|x64
{92D76120-5578-484A-BB93-24D105B48043}.Release|x64.Deploy.0 = Release|x64
{92D76120-5578-484A-BB93-24D105B48043}.Release|x86.ActiveCfg = Release|x86
{92D76120-5578-484A-BB93-24D105B48043}.Release|x86.Build.0 = Release|x86
{92D76120-5578-484A-BB93-24D105B48043}.Release|x86.Deploy.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8831C984-1B2F-462D-916A-68826955596B}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,7 @@
<Application
x:Class="Audio_Router.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Audio_Router">
</Application>

View File

@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Media;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace Audio_Router
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
sealed partial class App : Application
{
public static App appRef;
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
appRef = this;
audioGraphConnections = new List<AudioGraphConnection>();
this.Suspending += OnSuspending;
this.EnteredBackground += App_EnteredBackground;
this.LeavingBackground += App_LeavingBackground;
Windows.System.MemoryManager.AppMemoryUsageLimitChanging += MemoryManager_AppMemoryUsageLimitChanging; ;
}
private void MemoryManager_AppMemoryUsageLimitChanging(object sender, Windows.System.AppMemoryUsageLimitChangingEventArgs e)
{
// If app memory usage is over the limit, reduce usage within 2 seconds
// so that the system does not suspend the app
if (Windows.System.MemoryManager.AppMemoryUsage >= e.NewLimit)
{
Debug.WriteLine(e.NewLimit + " " + Windows.System.MemoryManager.AppMemoryUsage);
}
}
// The list of audio graphs
public List<AudioGraphConnection> audioGraphConnections;
private void App_LeavingBackground(object sender, LeavingBackgroundEventArgs e)
{
isInBackground = false;
}
private void App_EnteredBackground(object sender, EnteredBackgroundEventArgs e)
{
isInBackground = true;
}
public static Windows.System.Display.DisplayRequest displayRequest = new Windows.System.Display.DisplayRequest();
bool isInBackground = false;
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
// Ensure the current window is active
Window.Current.Activate();
}
}
/// <summary>
/// Invoked when Navigation to a certain page fails
/// </summary>
/// <param name="sender">The Frame which failed navigation</param>
/// <param name="e">Details about the navigation failure</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
/// <summary>
/// Invoked when application execution is being suspended. Application state is saved
/// without knowing whether the application will be terminated or resumed with the contents
/// of memory still intact.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
deferral.Complete();
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,184 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{92D76120-5578-484A-BB93-24D105B48043}</ProjectGuid>
<OutputType>AppContainerExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Audio_Router</RootNamespace>
<AssemblyName>Audio Router</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.19041.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WindowsXamlEnableOverview>true</WindowsXamlEnableOverview>
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
<PackageCertificateThumbprint>CED012D19342E23CEB15B1111E6E590A9225BF36</PackageCertificateThumbprint>
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
<GenerateTestArtifacts>True</GenerateTestArtifacts>
<AppxBundle>Always</AppxBundle>
<AppxBundlePlatforms>x86|x64|arm</AppxBundlePlatforms>
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM64'">
<OutputPath>bin\ARM64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="AudioGraphConnection.cs" />
<Compile Include="AudioPlayback.cs" />
<Compile Include="MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
</ItemGroup>
<ItemGroup>
<Content Include="Properties\Default.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="MainPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.12</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<None Include="..\.editorconfig">
<Link>.editorconfig</Link>
</None>
<None Include="Audio Router_TemporaryKey.pfx" />
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Media.Audio;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Documents;
using Windows.UI.Xaml.Media;
namespace Audio_Router
{
/// <summary>
/// Store the data graph in a minimal data class, so that the application meets memory requirements for a
/// background task
/// </summary>
public class AudioGraphConnection
{
string SourceNames { get; }
string TargetNames { get; }
int SamplesPerMS { get; }
public AudioGraph Graph { get; private set; }
AudioDeviceOutputNode TargetDevice { get; set; }
AudioDeviceInputNode SourceDevice { get; set; }
CoreDispatcher uiDispatcher { get; set; }
TextBlock latencyLabel { get; set; }
int quantaCount = 0;
public AudioGraphConnection(AudioGraph graph, AudioDeviceOutputNode targetDevice, AudioDeviceInputNode sourceDevice, CoreDispatcher uiDispatcher)
{
Graph = graph;
TargetDevice = targetDevice;
SourceDevice = sourceDevice;
this.uiDispatcher = uiDispatcher;
SourceNames = sourceDevice.Device.Name;
TargetNames = targetDevice.Device.Name;
SamplesPerMS = (int)(Graph.SamplesPerQuantum / (double)Graph.EncodingProperties.SampleRate * 1000);
}
/// <summary>
/// Creates a grid populated with UI controls containing information about this item, i.e. for rendering
/// </summary>
/// <returns></returns>
public Grid GetAsDisplayItem(out Button deletionButton)
{
// Create the display elements
Grid result = new Grid();
Button destroyButton = new Button()
{
Content = "Stop",
HorizontalAlignment = HorizontalAlignment.Right
};
TextBlock label = new TextBlock()
{
HorizontalAlignment = HorizontalAlignment.Left
};
latencyLabel = new TextBlock()
{
Padding = new Thickness(0, 0, 5, 0)
};
Slider volumeSlider = new Slider
{
Maximum = 5.0,
Minimum = 0.0,
Value = 1.0,
StepFrequency = 0.1,
MinWidth = 250,
Margin = new Thickness(5, 0, 5, 0)
};
StackPanel rightSide = new StackPanel
{
Orientation = Orientation.Horizontal
};
// Set the base grid to span the full screen width available
result.HorizontalAlignment = HorizontalAlignment.Stretch;
result.Margin = new Thickness(1, 1, 1, 5);
// Add Column definitions
result.ColumnDefinitions.Add(new ColumnDefinition()); // '*' size; fits the maximum space it can, evenly
result.ColumnDefinitions.Add(new ColumnDefinition() // 'auto'; automatically resizes to the total size of the children elements
{
Width = GridLength.Auto
});
Run sourceDev = new Run()
{
Text = SourceNames
};
Run arrow = new Run
{
Text = "  ",
FontFamily = new FontFamily("Segoe MDL2 Assets")
};
Run targetDev = new Run()
{
Text = TargetNames
};
// Add the text to the label
label.Inlines.Add(sourceDev);
label.Inlines.Add(arrow);
label.Inlines.Add(targetDev);
// Register the event
destroyButton.Click += DestroyButton_Click;
result.Children.Add(label);
volumeSlider.ValueChanged += VolumeSlider_ValueChanged;
Graph.QuantumProcessed += Graph_QuantumProcessed;
rightSide.Children.Add(latencyLabel);
rightSide.Children.Add(volumeSlider);
rightSide.Children.Add(destroyButton);
rightSide.HorizontalAlignment = HorizontalAlignment.Right;
result.Children.Add(rightSide);
Grid.SetColumn(rightSide, 1);
deletionButton = destroyButton;
return result;
}
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)
{
string result = (Graph.LatencyInSamples / (double)Graph.SamplesPerQuantum * SamplesPerMS).ToString("0.#") + " ms";
if (latencyLabel is null) return;
_ = uiDispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
{
try
{
latencyLabel.Text = result;
}
catch
{
}
});
quantaCount = 0;
}
}
private void VolumeSlider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
TargetDevice.OutgoingGain = e.NewValue;
SourceDevice.OutgoingGain = e.NewValue;
}
private void DestroyButton_Click(object sender, RoutedEventArgs e)
{
latencyLabel = null;
Graph.QuantumProcessed -= Graph_QuantumProcessed;
Graph.Stop();
TargetDevice.Dispose();
SourceDevice.Dispose();
Graph.Dispose();
Graph = null;
TargetDevice = null;
SourceDevice = null;
uiDispatcher = null;
_ = App.appRef.audioGraphConnections.Remove(this);
GC.Collect();
}
public void OnHide()
{
latencyLabel = null;
}
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Media.Playback;
namespace Audio_Router
{
class AudioPlayback
{
public MediaPlayer Player { get; private set; }
static AudioPlayback instance;
public static AudioPlayback Instance
{
get
{
if (instance is null)
{
instance = new AudioPlayback();
}
return instance;
}
}
private AudioPlayback()
{
Player = new MediaPlayer()
{
AutoPlay = false
};
}
}
}

View File

@ -0,0 +1,34 @@
<Page
x:Class="Audio_Router.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Audio_Router"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.Background>
<AcrylicBrush TintColor="#CC000000" TintOpacity="0.75"/>
</Grid.Background>
<StackPanel x:Name="SourceFlyout" Padding="20,30,20,20" Margin="0,18,0,0" Grid.RowSpan="1" VerticalAlignment="Stretch">
<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"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,20,0,5">
<Button x:Name="startStreamButton" Content="Add Audio Graph" HorizontalContentAlignment="Center" Background="#54FFFFFF" Click="startStreamButton_Click"/>
</StackPanel>
</StackPanel>
<ScrollViewer Grid.Row="1" Margin="10,5,10,5" HorizontalScrollMode="Disabled" Padding="0,0,10,0">
<StackPanel x:Name="CreatedGraphs"/>
</ScrollViewer>
</Grid>
</Page>

View File

@ -0,0 +1,256 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Media;
using Windows.Media.Audio;
using Windows.Media.Capture;
using Windows.Media.Devices;
using Windows.Media.Playback;
using Windows.Media.Render;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Documents;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace Audio_Router
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
//AudioGraph graph;
// The list of audio devices
DeviceInformationCollection sourceDevices;
DeviceInformationCollection targetDevices;
public MainPage()
{
this.InitializeComponent();
App.appRef.Suspending += AppRef_Suspending;
App.appRef.Resuming += AppRef_Resuming;
App.appRef.EnteredBackground += AppRef_EnteredBackground;
App.appRef.LeavingBackground += AppRef_LeavingBackground;
}
private void AppRef_LeavingBackground(object sender, Windows.ApplicationModel.LeavingBackgroundEventArgs e)
{
if (IsLoaded)
{
BackgroundRestore();
}
}
private void AppRef_EnteredBackground(object sender, Windows.ApplicationModel.EnteredBackgroundEventArgs e)
{
BackgroundCleanup();
}
private void AppRef_Resuming(object sender, object e)
{
BackgroundRestore();
}
/// <summary>
/// Clear existing elements from this display, to free memory
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AppRef_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
{
BackgroundCleanup();
}
private void BackgroundCleanup()
{
// Clear all items from the lists
InputAudioComboBox.Items.Clear();
OutputAudioComboBox.Items.Clear();
CreatedGraphs.Children.Clear();
// Ensure the GC collects freed memory, by explicitly calling the GC
GC.Collect();
}
private void BackgroundRestore()
{
// Start the task that populates the lists
if (InputAudioComboBox.Items.Count > 0 || OutputAudioComboBox.Items.Count > 0) return;
_ = Task.Run(() => FindAudioSources());
// And add existing graphs back to the page
foreach (AudioGraphConnection graphConneciton in App.appRef.audioGraphConnections)
{
Grid renderGrid = graphConneciton.GetAsDisplayItem(out Button visualButton);
CreatedGraphs.Children.Add(renderGrid);
void handler(object sender, RoutedEventArgs e)
{
visualButton.Click -= handler;
DestroyVisualElement(ref renderGrid);
}
visualButton.Click += handler;
}
}
/// <summary>
/// When the page is navigated to, populate the list
/// </summary>
/// <param name="e"></param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// Populate audio device list; as a Task to avoid delaying creation
_ = Task.Run(() => FindAudioSources());
}
private async Task FindAudioSources()
{
// First, clear the lists
_ = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
InputAudioComboBox.Items.Clear();
OutputAudioComboBox.Items.Clear();
});
// Perform this on our spawned thread, as the operation is asynchronous-synchronous await
sourceDevices = await DeviceInformation.FindAllAsync(MediaDevice.GetAudioCaptureSelector());
targetDevices = await DeviceInformation.FindAllAsync(MediaDevice.GetAudioRenderSelector());
DeviceInformation defaultOutput = await DeviceInformation.CreateFromIdAsync(MediaDevice.GetDefaultAudioRenderId(AudioDeviceRole.Default));
DeviceInformation defaultInput = await DeviceInformation.CreateFromIdAsync(MediaDevice.GetDefaultAudioCaptureId(AudioDeviceRole.Default));
_ = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// Set the Input device
for (int i = 0; i < sourceDevices.Count; i++)
{
if (sourceDevices[i].Id == defaultInput.Id)
{
InputAudioComboBox.Items.Add(sourceDevices[i].Name + " (Default Device)");
InputAudioComboBox.SelectedIndex = i;
}
else
{
InputAudioComboBox.Items.Add(sourceDevices[i].Name);
}
}
for (int i = 0; i < targetDevices.Count; i++)
{
if(targetDevices[i].Id == defaultOutput.Id)
{
OutputAudioComboBox.Items.Add(targetDevices[i].Name + " (Default Device)");
OutputAudioComboBox.SelectedIndex = i;
}
else
{
OutputAudioComboBox.Items.Add(targetDevices[i].Name);
}
}
});
}
private async void startStreamButton_Click(object sender, RoutedEventArgs e)
{
await CreateAudioGraph();
}
private async Task CreateAudioGraph()
{
App.displayRequest.RequestActive();
DeviceInformation outputDevice = targetDevices[OutputAudioComboBox.SelectedIndex];
DeviceInformation inputDevice = sourceDevices[InputAudioComboBox.SelectedIndex];
AudioGraphSettings settings = LowLatencyCheckbox.IsChecked == true
? new AudioGraphSettings(AudioRenderCategory.Media)
{
QuantumSizeSelectionMode = QuantumSizeSelectionMode.ClosestToDesired,
DesiredSamplesPerQuantum = 10,
PrimaryRenderDevice = outputDevice,
DesiredRenderDeviceAudioProcessing = Windows.Media.AudioProcessing.Raw,
EncodingProperties = new Windows.Media.MediaProperties.AudioEncodingProperties()
{
Subtype = "Float",
SampleRate = 128000, // Manually set to 128 000 samples/second so that the delay is significantly reduced
ChannelCount = 2,
BitsPerSample = 32,
Bitrate = 3072000
}
}
: new AudioGraphSettings(AudioRenderCategory.Media)
{
PrimaryRenderDevice = outputDevice
};
CreateAudioGraphResult result = await AudioGraph.CreateAsync(settings);
AudioGraph ag = result.Graph;
if (result.Status != AudioGraphCreationStatus.Success)
{
return;
}
CreateAudioDeviceInputNodeResult deviceInputNodeResult = await ag.CreateDeviceInputNodeAsync(MediaCategory.Other, ag.EncodingProperties, inputDevice);
if (deviceInputNodeResult.Status != AudioDeviceNodeCreationStatus.Success)
{
// Fail-safe, switch to using the default encoding properties
settings = new AudioGraphSettings(AudioRenderCategory.Media)
{
PrimaryRenderDevice = outputDevice
};
result = await AudioGraph.CreateAsync(settings);
ag = result.Graph;
if (result.Status != AudioGraphCreationStatus.Success)
{
return;
}
deviceInputNodeResult = await ag.CreateDeviceInputNodeAsync(MediaCategory.Other, ag.EncodingProperties, inputDevice);
if (deviceInputNodeResult.Status != AudioDeviceNodeCreationStatus.Success)
{
return;
}
}
// Create the device output node connection
CreateAudioDeviceOutputNodeResult deviceOutputNodeResult = await ag.CreateDeviceOutputNodeAsync();
if (deviceOutputNodeResult.Status != AudioDeviceNodeCreationStatus.Success)
{
return;
}
deviceInputNodeResult.DeviceInputNode.AddOutgoingConnection(deviceOutputNodeResult.DeviceOutputNode);
// appMediaControls.PlaybackStatus = MediaPlaybackStatus.Playing;
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);
// Inline decl to add a second listener to the click event - will remove the grid from the display
void handler(object sender, RoutedEventArgs e)
{
visualButton.Click -= handler;
DestroyVisualElement(ref renderGrid);
}
visualButton.Click += handler;
}
private void DestroyVisualElement(ref Grid element)
{
_ = CreatedGraphs.Children.Remove(element);
element = null;
}
}
}

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
IgnorableNamespaces="uap mp uap3">
<Identity
Name="21ddea4f-7f1d-4d81-95a9-78bd1ad38f13"
Publisher="CN=Brychan Dempsey"
Version="1.2.3.0" />
<mp:PhoneIdentity PhoneProductId="21ddea4f-7f1d-4d81-95a9-78bd1ad38f13" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>Audio Router</DisplayName>
<PublisherDisplayName>Brychan Dempsey</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="Audio_Router.App">
<uap:VisualElements
DisplayName="Audio Router"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png"
Description="Audio Router"
BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
<uap3:Capability Name="backgroundMediaPlayback"/>
<DeviceCapability Name="microphone"/>
</Capabilities>
</Package>

View File

@ -0,0 +1,29 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Audio Router")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Audio Router")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]

View File

@ -0,0 +1,31 @@
<!--
This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
developers. However, you can modify these parameters to modify the behavior of the .NET Native
optimizer.
Runtime Directives are documented at https://go.microsoft.com/fwlink/?LinkID=391919
To fully enable reflection for App1.MyClass and all of its public/private members
<Type Name="App1.MyClass" Dynamic="Required All"/>
To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
Using the Namespace directive to apply reflection policy to all the types in a particular namespace
<Namespace Name="DataClasses.ViewModels" Serialize="All" />
-->
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<!--
An Assembly element with Name="*Application*" applies to all assemblies in
the application package. The asterisks are not wildcards.
-->
<Assembly Name="*Application*" Dynamic="Required All" />
<!-- Add your application specific runtime directives here. -->
</Application>
</Directives>

View File

@ -0,0 +1,9 @@
<Application x:Class="Audio_Router_WPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Audio_Router_WPF"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace Audio_Router_WPF
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<RootNamespace>Audio_Router_WPF</RootNamespace>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,12 @@
<Window x:Class="Audio_Router_WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
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">
<Grid>
</Grid>
</Window>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Audio_Router_WPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}

77
Audio Router.sln Normal file
View File

@ -0,0 +1,77 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31710.8
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Audio Router UWP", "Audio Router UWP\Audio Router\Audio Router UWP.csproj", "{92D76120-5578-484A-BB93-24D105B48043}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Audio Router WPF", "Audio Router WPF\Audio Router WPF.csproj", "{E0BEDD27-7800-4434-BA2D-7D71964F557F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{92D76120-5578-484A-BB93-24D105B48043}.Debug|Any CPU.ActiveCfg = Debug|x86
{92D76120-5578-484A-BB93-24D105B48043}.Debug|ARM.ActiveCfg = Debug|ARM
{92D76120-5578-484A-BB93-24D105B48043}.Debug|ARM.Build.0 = Debug|ARM
{92D76120-5578-484A-BB93-24D105B48043}.Debug|ARM.Deploy.0 = Debug|ARM
{92D76120-5578-484A-BB93-24D105B48043}.Debug|ARM64.ActiveCfg = Debug|ARM64
{92D76120-5578-484A-BB93-24D105B48043}.Debug|ARM64.Build.0 = Debug|ARM64
{92D76120-5578-484A-BB93-24D105B48043}.Debug|ARM64.Deploy.0 = Debug|ARM64
{92D76120-5578-484A-BB93-24D105B48043}.Debug|x64.ActiveCfg = Debug|x64
{92D76120-5578-484A-BB93-24D105B48043}.Debug|x64.Build.0 = Debug|x64
{92D76120-5578-484A-BB93-24D105B48043}.Debug|x64.Deploy.0 = Debug|x64
{92D76120-5578-484A-BB93-24D105B48043}.Debug|x86.ActiveCfg = Debug|x86
{92D76120-5578-484A-BB93-24D105B48043}.Debug|x86.Build.0 = Debug|x86
{92D76120-5578-484A-BB93-24D105B48043}.Debug|x86.Deploy.0 = Debug|x86
{92D76120-5578-484A-BB93-24D105B48043}.Release|Any CPU.ActiveCfg = Release|x86
{92D76120-5578-484A-BB93-24D105B48043}.Release|ARM.ActiveCfg = Release|ARM
{92D76120-5578-484A-BB93-24D105B48043}.Release|ARM.Build.0 = Release|ARM
{92D76120-5578-484A-BB93-24D105B48043}.Release|ARM.Deploy.0 = Release|ARM
{92D76120-5578-484A-BB93-24D105B48043}.Release|ARM64.ActiveCfg = Release|ARM64
{92D76120-5578-484A-BB93-24D105B48043}.Release|ARM64.Build.0 = Release|ARM64
{92D76120-5578-484A-BB93-24D105B48043}.Release|ARM64.Deploy.0 = Release|ARM64
{92D76120-5578-484A-BB93-24D105B48043}.Release|x64.ActiveCfg = Release|x64
{92D76120-5578-484A-BB93-24D105B48043}.Release|x64.Build.0 = Release|x64
{92D76120-5578-484A-BB93-24D105B48043}.Release|x64.Deploy.0 = Release|x64
{92D76120-5578-484A-BB93-24D105B48043}.Release|x86.ActiveCfg = Release|x86
{92D76120-5578-484A-BB93-24D105B48043}.Release|x86.Build.0 = Release|x86
{92D76120-5578-484A-BB93-24D105B48043}.Release|x86.Deploy.0 = Release|x86
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Debug|ARM.ActiveCfg = Debug|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Debug|ARM.Build.0 = Debug|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Debug|ARM64.Build.0 = Debug|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Debug|x64.ActiveCfg = Debug|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Debug|x64.Build.0 = Debug|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Debug|x86.ActiveCfg = Debug|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Debug|x86.Build.0 = Debug|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Release|Any CPU.Build.0 = Release|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Release|ARM.ActiveCfg = Release|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Release|ARM.Build.0 = Release|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Release|ARM64.ActiveCfg = Release|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Release|ARM64.Build.0 = Release|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Release|x64.ActiveCfg = Release|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Release|x64.Build.0 = Release|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Release|x86.ActiveCfg = Release|Any CPU
{E0BEDD27-7800-4434-BA2D-7D71964F557F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {725876A6-2151-4F75-A3E5-9522B152A17C}
EndGlobalSection
EndGlobal