2021-09-23 17:30:38 +12:00
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 ;
2021-09-23 19:21:08 +12:00
using Windows.UI.Core ;
2021-09-23 17:30:38 +12:00
using System.Windows.Shapes ;
2021-09-23 19:21:08 +12:00
using Windows.Devices.Enumeration ;
using Windows.Media.Audio ;
using System.Windows.Threading ;
using Windows.Media.Devices ;
using Windows.Media.Render ;
using Windows.Media.Capture ;
2021-09-23 17:30:38 +12:00
namespace Audio_Router_WPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
2021-09-23 19:21:08 +12:00
//AudioGraph graph;
// The list of audio devices
DeviceInformationCollection sourceDevices ;
DeviceInformationCollection targetDevices ;
2021-10-04 23:10:43 +13:00
#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
2021-09-23 17:30:38 +12:00
public MainWindow ( )
2021-09-23 19:21:08 +12:00
{
2021-09-23 17:30:38 +12:00
InitializeComponent ( ) ;
2021-10-04 23:10:43 +13:00
}
protected override void OnInitialized ( EventArgs e )
{
base . OnInitialized ( e ) ;
2021-09-23 19:21:08 +12:00
_ = 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 ) ) ;
2021-10-04 23:10:43 +13:00
2021-09-23 19:21:08 +12:00
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 ) ;
}
}
2021-10-04 23:10:43 +13:00
// _ = InputAudioComboBox.Items.Add("--Output nodes--"); // This requires further work, implementing W
2021-09-23 19:21:08 +12:00
for ( int i = 0 ; i < targetDevices . Count ; i + + )
{
if ( targetDevices [ i ] . Id = = defaultOutput . Id )
{
_ = OutputAudioComboBox . Items . Add ( targetDevices [ i ] . Name + " (Default Device)" ) ;
2021-10-04 23:10:43 +13:00
//_ = InputAudioComboBox.Items.Add(targetDevices[i].Name + " (Default Device)");
2021-09-23 19:21:08 +12:00
OutputAudioComboBox . SelectedIndex = i ;
}
else
{
_ = OutputAudioComboBox . Items . Add ( targetDevices [ i ] . Name ) ;
2021-10-04 23:10:43 +13:00
//_ = InputAudioComboBox.Items.Add(targetDevices[i].Name);
2021-09-23 19:21:08 +12:00
}
}
} ) ;
2021-09-23 17:30:38 +12:00
}
2021-09-23 19:21:08 +12:00
private async Task CreateAudioGraph ( )
{
App . displayRequest . RequestActive ( ) ;
2021-10-04 23:10:43 +13:00
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 ) ] ;
}
2021-09-23 19:21:08 +12:00
DeviceInformation outputDevice = targetDevices [ OutputAudioComboBox . SelectedIndex ] ;
2021-10-04 23:10:43 +13:00
2021-09-23 19:21:08 +12:00
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 ;
}
2021-10-04 23:12:46 +13:00
CreateAudioDeviceInputNodeResult deviceInputNodeResult = await ag . CreateDeviceInputNodeAsync ( MediaCategory . Other , ag . EncodingProperties , inputDevice ) ;
//CreateAudioDeviceInputNodeResult deviceInputNodeResult = await ag.CreateDeviceInputNodeAsync(,,);
//AudioFrameInputNode g = ag.CreateFrameInputNode();
2021-09-23 19:21:08 +12:00
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 ( ) ;
2021-10-04 23:10:43 +13:00
//deviceOutputNodeResult.DeviceOutputNode.Creat
2021-09-23 19:21:08 +12:00
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 ) ;
}
2021-10-04 23:10:43 +13:00
2021-09-23 19:21:08 +12:00
visualButton . Click + = handler ;
}
private void DestroyVisualElement ( ref Grid element )
{
CreatedGraphs . Children . Remove ( element ) ;
element = null ;
}
2021-09-23 17:30:38 +12:00
}
}