using System.IO;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using NAudio.CoreAudioApi;
using NAudio.Wave;
using WebSocketSharp;

namespace GameStream.Client;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    private WebSocket? _wsStream;
    private WebSocket? _wsSettings;
    private WebSocket? _wsInput;
    private WebSocket? _wsAudio;

    private BufferedWaveProvider? _bufferedWaveProvider;
    private WaveOut _waveOut;
    private VolumeSampleProvider _volumeProvider;

    public MainWindow()
    {
        InitializeComponent();
        ConnectToServer();

        MouseMove += MainWindow_MouseMove;
        MouseDown += MainWindow_MouseDown;
        MouseUp += MainWindow_MouseUp;
        KeyDown += MainWindow_KeyDown;
        KeyUp += MainWindow_KeyUp;

        Closed += OnClosed;

        RenderCursorCheckBox.Checked += (s, e) => UpdateStreamSettings(true);
        RenderCursorCheckBox.Unchecked += (s, e) => UpdateStreamSettings(false);

        DebugPanelCheckBox.Checked += (s, e) => ToggleDebugPanel(true);
        DebugPanelCheckBox.Unchecked += (s, e) => ToggleDebugPanel(false);

        BlockInputCheckBox.Checked += (s, e) => ToggleBlockInput(true);
        BlockInputCheckBox.Unchecked += (s, e) => ToggleBlockInput(false);
    }

    private void ToggleDebugPanel(bool b)
    {
        DebugTextBlock.Visibility = b ? Visibility.Visible : Visibility.Collapsed;
    }

    private void UpdateStreamSettings(bool b)
    {
        Send("RENDER_WITH_CURSOR", RenderCursorCheckBox.IsChecked);
    }

    private void ToggleBlockInput(bool b)
    {
        Send("BLOCK_INPUT", BlockInputCheckBox.IsChecked);
    }

    private void MainWindow_MouseMove(object sender, MouseEventArgs e)
    {
        if (_wsInput == null || !_wsInput.IsAlive) return;

        var position = e.GetPosition(this);
        var message = $"MOUSE_MOVE:{position.X}:{position.Y}";
        _wsInput.Send(message);
    }

    private void MainWindow_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (_wsInput == null || !_wsInput.IsAlive) return;

        var button = e.ChangedButton.ToString();
        var message = $"MOUSE_DOWN:{button}";
        _wsInput.Send(message);
    }

    private void MainWindow_MouseUp(object sender, MouseButtonEventArgs e)
    {
        if (_wsInput == null || !_wsInput.IsAlive) return;

        var button = e.ChangedButton.ToString();
        var message = $"MOUSE_UP:{button}";
        _wsInput.Send(message);
    }

    private void MainWindow_KeyDown(object sender, KeyEventArgs e)
    {
        if (_wsInput == null || !_wsInput.IsAlive) return;

        var message = $"KEY_DOWN:{e.Key}";
        _wsInput.Send(message);
    }

    private void MainWindow_KeyUp(object sender, KeyEventArgs e)
    {
        if (_wsInput == null || !_wsInput.IsAlive) return;

        var message = $"KEY_UP:{e.Key}";
        _wsInput.Send(message);
    }

    private void ConnectToServer()
    {
        _wsStream = new WebSocket("ws://127.0.0.1:8080/stream");
        _wsStream.OnMessage += (sender, e) => { Application.Current.Dispatcher.Invoke(() => UpdateImage(e.RawData)); };
        _wsStream.Connect();

        _wsInput = new WebSocket("ws://127.0.0.1:8080/input");
        _wsInput.Connect();

        _wsSettings = new WebSocket("ws://127.0.0.1:8080/settings");
        _wsSettings.Connect();

        _wsAudio = new WebSocket("ws://127.0.0.1:8080/audio");
        _wsAudio.OnMessage += (sender, e) => ReceiveAudioStream(e.RawData);
        _wsAudio.Connect();

        SetupAudioPlayback();

        UpdateDebugMessage($"1: {RenderCursorCheckBox.IsChecked}, 2: {BlockInputCheckBox.IsChecked}");
        Send("RENDER_WITH_CURSOR", RenderCursorCheckBox.IsChecked);
        Send("BLOCK_INPUT", BlockInputCheckBox.IsChecked);
    }

    private void SetupAudioPlayback()
    {
        var enumerator = new MMDeviceEnumerator();
        var defaultDevice = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
        var mixFormat = defaultDevice.AudioClient.MixFormat;
        
        _bufferedWaveProvider = new BufferedWaveProvider(new WaveFormat(mixFormat.SampleRate, 16, mixFormat.Channels));
        var sampleProvider = _bufferedWaveProvider.ToSampleProvider();
        _volumeProvider = new VolumeSampleProvider(sampleProvider);

        _volumeProvider.Volume = 0.1f;
        _waveOut = new WaveOut();
        _waveOut.Init(_volumeProvider);
        _waveOut.Play();
    }

    private void ReceiveAudioStream(byte[] audioData)
    {
        _bufferedWaveProvider?.AddSamples(audioData, 0, audioData.Length);
    }

    private void Send<T>(string id, T value)
    {
        _wsSettings?.Send($"{id}:{value}");
    }

    private void UpdateStatusMessage(string message)
    {
        StatusMessage.Text = message;
    }

    private void UpdateDebugMessage(string message)
    {
        DebugTextBlock.Text = message;
    }

    private void UpdateImage(byte[] data)
    {
        using var ms = new MemoryStream(data);
        var bitmap = new BitmapImage();
        bitmap.BeginInit();
        bitmap.CacheOption = BitmapCacheOption.OnLoad;
        bitmap.StreamSource = ms;
        bitmap.EndInit();
        StreamImage.Source = bitmap;
    }

    private void OnClosed(object? sender, EventArgs e)
    {
        _wsStream?.Close();
        _wsInput?.Close();
        _wsSettings?.Close();
        _wsAudio?.Close();
        _waveOut?.Dispose();
    }
}

почему звуК дико фонит?

using NAudio.Wave;
using WebSocketSharp;
using WebSocketSharp.Server;

namespace GameStream.Server;

public class AudioStreamer : WebSocketBehavior
{
    private WasapiLoopbackCapture? _waveIn;
    private BufferedWaveProvider? _waveProvider;
    private MemoryStream? _audioStream;

    protected override void OnOpen()
    {
        Console.WriteLine("Audio connection opened.");

        _waveIn = new WasapiLoopbackCapture();
        _waveProvider = new BufferedWaveProvider(_waveIn.WaveFormat);

        _waveIn.DataAvailable += (s, a) =>
        {
            _waveProvider.AddSamples(a.Buffer, 0, a.BytesRecorded);
                
            _audioStream = new MemoryStream(a.BytesRecorded);
            _audioStream.Write(a.Buffer, 0, a.BytesRecorded);
            _audioStream.Position = 0;

            Send(_audioStream.ToArray());
        };

        _waveIn.StartRecording();
    }

    protected override void OnClose(CloseEventArgs e)
    {
        Console.WriteLine("Audio connection closed.");

        _waveIn?.StopRecording();
        _waveIn?.Dispose();
        _audioStream?.Dispose();
    }
}