203 lines
8.6 KiB
C#
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;
|
|
}
|
|
|
|
}
|
|
}
|