178 lines
5.8 KiB
C#
178 lines
5.8 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using System.Text;
|
|||
|
using System.Windows.Threading;
|
|||
|
using System.Threading.Tasks;
|
|||
|
using Windows.Media.Audio;
|
|||
|
using System.Windows.Controls;
|
|||
|
using System.Windows;
|
|||
|
using System.Windows.Documents;
|
|||
|
using System.Windows.Media;
|
|||
|
|
|||
|
namespace Audio_Router_WPF
|
|||
|
{
|
|||
|
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; }
|
|||
|
|
|||
|
Dispatcher uiDispatcher { get; set; }
|
|||
|
|
|||
|
TextBlock latencyLabel { get; set; }
|
|||
|
|
|||
|
int quantaCount = 0;
|
|||
|
|
|||
|
public AudioGraphConnection(AudioGraph graph, AudioDeviceOutputNode targetDevice, AudioDeviceInputNode sourceDevice, Dispatcher 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,
|
|||
|
SmallChange = 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.Invoke(() =>
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
latencyLabel.Text = result;
|
|||
|
}
|
|||
|
catch
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
}, DispatcherPriority.Background);
|
|||
|
quantaCount = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void VolumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> 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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|