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 Windows.UI.Core; using System.Windows.Shapes; using Windows.Devices.Enumeration; using Windows.Media.Audio; using System.Windows.Threading; using Windows.Media.Devices; using Windows.Media.Render; using Windows.Media.Capture; namespace Audio_Router_WPF { /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window { //AudioGraph graph; // The list of audio devices DeviceInformationCollection sourceDevices; DeviceInformationCollection targetDevices; public MainWindow() { InitializeComponent(); _ = Task.Run(() => FindAudioSources()); } private async void AddAudioGraphButton_Click(object sender, RoutedEventArgs e) { await CreateAudioGraph(); } private async Task FindAudioSources() { // First, clear the lists Dispatcher.Invoke(() => { InputAudioComboBox.Items.Clear(); OutputAudioComboBox.Items.Clear(); }, DispatcherPriority.Normal); // 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.Invoke(() => { // 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 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; } } }