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); } /// /// Creates a grid populated with UI controls containing information about this item, i.e. for rendering /// /// 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 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; } } }