Audio-Router/Audio Router WPF/MainWindow.xaml.cs

203 lines
8.6 KiB
C#

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
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
//AudioGraph graph;
// 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();
}
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);
}
}
// _ = 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);
}
}
});
}
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];
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);
//CreateAudioDeviceInputNodeResult deviceInputNodeResult = await ag.CreateDeviceInputNodeAsync(,,);
//AudioFrameInputNode g = ag.CreateFrameInputNode();
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();
//deviceOutputNodeResult.DeviceOutputNode.Creat
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;
}
}
}