Temp

using HidLibrary;
using NAudio.CoreAudioApi;
using System.Threading;

namespace UsbAudioControl;

/// <summary>
/// HID 音频设备控制器
/// 使用 HidLibrary 控制 LED,使用 Core Audio 监听系统静音状态
/// </summary>
public class HidAudioController : IAudioMuteController
{
    private HidDevice? _hidDevice;
    private HidDeviceInfo? _connectedDeviceInfo;
    private MMDevice? _audioDevice;
    private bool _disposed;
    private int _disposeSignaled; // 0=未释放, 1=已触发释放
    private bool _isMonitoring;
    private bool _lastMuteState;
    private float _lastVolume;
    private readonly object _lock = new();
    private System.Threading.Timer? _hidPollingTimer;
    private readonly HidAudioConfig _config;
    private DateTime _lastButtonPressTime = DateTime.MinValue;
    private const int ButtonDebounceMs = 300; // 按键防抖时间 (毫秒)
    private DateTime _connectTime = DateTime.MinValue;
    private const int IgnoreReportsAfterConnectMs = 500; // 连接后忽略报告的时间 (毫秒)

    /// <summary>
    /// 当前配置
    /// </summary>
    public HidAudioConfig Config => _config;

    public AudioDeviceInfo? ConnectedDevice => _connectedDeviceInfo?.ToAudioDeviceInfo();
    public bool IsConnected => _hidDevice != null && _hidDevice.IsConnected;
    public bool SupportsMute => true;
    public bool SupportsVolume => false;
    public bool SupportsLed => true;

    public event EventHandler<AudioStateChangedEventArgs>? StateChanged;
    public event EventHandler<PhysicalButtonEventArgs>? PhysicalButtonPressed;

    /// <summary>
    /// 创建控制器 (使用默认配置)
    /// </summary>
    public HidAudioController() : this(HidAudioConfig.Default)
    {
    }

    /// <summary>
    /// 创建控制器 (使用自定义配置)
    /// </summary>
    public HidAudioController(HidAudioConfig config)
    {
        _config = config;
    }

    private void ThrowIfDisposed()
    {
        if (_disposed)
            throw new ObjectDisposedException(nameof(HidAudioController));
    }

    /// <summary>
    /// 查找所有 HID 音频设备 (使用默认配置的 VID/PID)
    /// </summary>
    public static List<HidDeviceInfo> FindAllHidAudioDevices()
    {
        return FindAllHidAudioDevices(HidAudioConfig.Default.VendorId, HidAudioConfig.Default.ProductId);
    }

    /// <summary>
    /// 查找所有 HID 音频设备
    /// </summary>
    public static List<HidDeviceInfo> FindAllHidAudioDevices(int vendorId, int productId)
    {
        var devices = new List<HidDeviceInfo>();

        var hidDevices = HidDevices.Enumerate(vendorId, productId)
            .Where(d => d.Capabilities.OutputReportByteLength > 0);

        foreach (var device in hidDevices)
        {
            try
            {
                var info = new HidDeviceInfo
                {
                    VendorId = (int)device.Attributes.VendorId,
                    ProductId = (int)device.Attributes.ProductId,
                    ProductName = device.Description ?? "Hamedal Speak A20",
                    DevicePath = device.DevicePath,
                    UsagePage = (ushort)device.Capabilities.UsagePage,
                    Usage = (ushort)device.Capabilities.Usage,
                    InputReportLength = device.Capabilities.InputReportByteLength,
                    OutputReportLength = device.Capabilities.OutputReportByteLength,
                    FeatureReportLength = device.Capabilities.FeatureReportByteLength
                };

                devices.Add(info);
            }
            finally
            {
                device.CloseDevice();
            }
        }

        return devices;
    }

    public IReadOnlyList<AudioDeviceInfo> EnumerateDevices()
    {
        ThrowIfDisposed();

        return FindAllHidAudioDevices(_config.VendorId, _config.ProductId)
            .Select(d => d.ToAudioDeviceInfo())
            .ToList();
    }

    public bool Connect(AudioDeviceInfo device)
    {
        ThrowIfDisposed();
        return ConnectByVidPid(device.VendorId, device.ProductId);
    }

    public bool ConnectByVidPid(int vendorId, int productId)
    {
        ThrowIfDisposed();
        Disconnect();

        // 连接 HID 设备
        var devices = HidDevices.Enumerate(vendorId, productId)
            .Where(d => d.Capabilities.OutputReportByteLength > 0)
            .ToList();

        int selectedIndex = devices.Count > 1 ? 1 : 0;
        _hidDevice = devices.Count > selectedIndex ? devices[selectedIndex] : devices.FirstOrDefault();

        if (_hidDevice == null)
            return false;

        _hidDevice.OpenDevice();

        if (!_hidDevice.IsConnected)
        {
            _hidDevice.Dispose();
            _hidDevice = null;
            return false;
        }

        _connectedDeviceInfo = new HidDeviceInfo
        {
            VendorId = (int)_hidDevice.Attributes.VendorId,
            ProductId = (int)_hidDevice.Attributes.ProductId,
            ProductName = _hidDevice.Description ?? "Hamedal Speak A20",
            DevicePath = _hidDevice.DevicePath,
            UsagePage = (ushort)_hidDevice.Capabilities.UsagePage,
            Usage = (ushort)_hidDevice.Capabilities.Usage,
            InputReportLength = _hidDevice.Capabilities.InputReportByteLength,
            OutputReportLength = _hidDevice.Capabilities.OutputReportByteLength
        };

        // 连接对应的 Core Audio 设备
        ConnectCoreAudioDevice();

        // 初始化状态 - 默认启用
        _lastMuteState = false;
        _lastVolume = GetVolumeFromSystem() ?? 1f;

        // 记录连接时间,用于忽略连接后的残留 HID 报告
        _connectTime = DateTime.Now;

        return true;
    }

    public bool ConnectByVidPid(string vidPid)
    {
        ThrowIfDisposed();

        if (string.IsNullOrEmpty(vidPid))
            return false;

        var parts = vidPid.Split(':');
        if (parts.Length != 2)
            return false;

        try
        {
            int vid = Convert.ToInt32(parts[0], 16);
            int pid = Convert.ToInt32(parts[1], 16);
            return ConnectByVidPid(vid, pid);
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// 自动检测当前系统默认输入麦克风,匹配配置并连接
    /// </summary>
    public static HidAudioController? ConnectAuto()
    {
        // 方式1: 根据当前系统默认麦克风匹配配置
        var defaultConfig = HidAudioConfigRegistry.GetDefaultMicrophoneConfig();
        if (defaultConfig != null)
        {
            Console.WriteLine($"使用配置: {defaultConfig.Name}");
            var controller = new HidAudioController(defaultConfig);
            if (controller.ConnectToFirst())
                return controller;
            Console.WriteLine("方式1连接失败,尝试方式2...");
            controller.Dispose();
        }

        // 方式2: 扫描所有已注册设备
        var devices = HidAudioConfigRegistry.ScanDevices();

        if (devices.Count == 0)
        {
            Console.WriteLine("未找到任何已注册的 HID 设备");
            return null;
        }

        var (config, devicePath) = devices[0];
        Console.WriteLine($"使用配置: {config.Name}");
        var controller2 = new HidAudioController(config);

        if (controller2.ConnectByPath(devicePath))
            return controller2;

        Console.WriteLine("方式2连接失败");
        controller2.Dispose();
        return null;
    }

    public bool ConnectToFirst()
    {
        ThrowIfDisposed();
        return ConnectByVidPid(_config.VendorId, _config.ProductId);
    }

    public bool ConnectByPath(string? devicePath)
    {
        ThrowIfDisposed();

        if (string.IsNullOrEmpty(devicePath))
            return false;

        Disconnect();

        var allDevices = HidDevices.Enumerate()
            .Where(d => d.Capabilities.OutputReportByteLength > 0);
        _hidDevice = allDevices.FirstOrDefault(d => d.DevicePath == devicePath);

        if (_hidDevice == null)
            return false;

        _hidDevice.OpenDevice();

        if (!_hidDevice.IsConnected)
        {
            _hidDevice.Dispose();
            _hidDevice = null;
            return false;
        }

        _connectedDeviceInfo = new HidDeviceInfo
        {
            VendorId = (int)_hidDevice.Attributes.VendorId,
            ProductId = (int)_hidDevice.Attributes.ProductId,
            ProductName = _hidDevice.Description ?? "HID Audio Device",
            DevicePath = _hidDevice.DevicePath,
            UsagePage = (ushort)_hidDevice.Capabilities.UsagePage,
            Usage = (ushort)_hidDevice.Capabilities.Usage,
            InputReportLength = _hidDevice.Capabilities.InputReportByteLength,
            OutputReportLength = _hidDevice.Capabilities.OutputReportByteLength
        };

        ConnectCoreAudioDevice();
        _lastMuteState = false;  // 默认启用状态

        // 记录连接时间,用于忽略连接后的残留 HID 报告
        _connectTime = DateTime.Now;

        return true;
    }

    /// <summary>
    /// 连接对应的 Core Audio 设备用于状态监听
    /// </summary>
    private void ConnectCoreAudioDevice()
    {
        try
        {
            using var enumerator = new MMDeviceEnumerator();
            var captureDevices = enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active);

            var keywords = _config.DeviceNameKeywords;

            // 查找匹配的设备
            if (keywords != null && keywords.Length > 0)
            {
                foreach (var device in captureDevices)
                {
                    if (keywords.Any(k => device.FriendlyName?.Contains(k) == true))
                    {
                        _audioDevice = device;
                        break;
                    }
                }
            }

            // 如果没找到,使用默认设备
            _audioDevice ??= enumerator.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Communications);
        }
        catch
        {
            // Core Audio 连接失败不影响 HID 控制
        }
    }

    public void Disconnect()
    {
        // 注意:这里不 ThrowIfDisposed,允许 Dispose() 内部调用清理
        lock (_lock)
        {
            StopMonitoringInternal();
            _audioDevice?.Dispose();
            _audioDevice = null;

            if (_hidDevice != null)
            {
                _hidDevice.CloseDevice();
                _hidDevice.Dispose();
                _hidDevice = null;
            }
            _connectedDeviceInfo = null;
        }
    }

    /// <summary>
    /// 设置静音状态
    /// </summary>
    public bool SetMute(bool mute)
    {
        ThrowIfDisposed();

        if (_hidDevice == null || !_hidDevice.IsConnected)
            return false;

        if (SendMuteCommand(mute))
        {
            _lastMuteState = mute;

            // 同步设置系统音频静音
            SetSystemMute(mute);

            return true;
        }

        return false;
    }

    /// <summary>
    /// 设置静音 LED(仅控制 LED,不影响系统音频)
    /// </summary>
    public bool SetMuteLed(bool muted)
    {
        ThrowIfDisposed();
        return SendMuteCommand(muted);
    }

    /// <summary>
    /// 获取静音状态(从系统读取)
    /// </summary>
    public bool? GetMute()
    {
        ThrowIfDisposed();
        return GetMuteFromSystem() ?? _lastMuteState;
    }

    /// <summary>
    /// 切换静音状态
    /// </summary>
    public bool? ToggleMute()
    {
        ThrowIfDisposed();

        var current = GetMute();
        if (current == null)
            return null;

        var newState = !current.Value;
        return SetMute(newState) ? newState : null;
    }

    public bool SetVolume(float volume)
    {
        ThrowIfDisposed();
        return false;
    }

    public float? GetVolume()
    {
        ThrowIfDisposed();
        return GetVolumeFromSystem();
    }

    /// <summary>
    /// 从系统获取静音状态
    /// </summary>
    private bool? GetMuteFromSystem()
    {
        if (_audioDevice == null)
            return null;

        try
        {
            return _audioDevice.AudioEndpointVolume.Mute;
        }
        catch
        {
            return null;
        }
    }

    /// <summary>
    /// 从系统获取音量
    /// </summary>
    private float? GetVolumeFromSystem()
    {
        if (_audioDevice == null)
            return null;

        try
        {
            return _audioDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
        }
        catch
        {
            return null;
        }
    }

    /// <summary>
    /// 设置系统静音状态
    /// </summary>
    private void SetSystemMute(bool mute)
    {
        if (_audioDevice == null)
            return;

        try
        {
            _audioDevice.AudioEndpointVolume.Mute = mute;
        }
        catch
        {
            // 忽略错误
        }
    }

    /// <summary>
    /// 发送静音命令到 HID 设备
    /// </summary>
    private bool SendMuteCommand(bool mute)
    {
        if (_hidDevice == null || !_hidDevice.IsConnected)
            return false;

        try
        {
            var reportData = new byte[_hidDevice.Capabilities.OutputReportByteLength];
            reportData[0] = _config.ReportId;
            reportData[1] = mute ? _config.MuteOnData : _config.MuteOffData;

            _hidDevice.Write(reportData);
            return true;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// 开始监听状态变化
    /// </summary>
    public void StartMonitoring()
    {
        ThrowIfDisposed();

        lock (_lock)
        {
            if (_isMonitoring || _hidDevice == null)
                return;

            _isMonitoring = true;
            _lastMuteState = GetMuteFromSystem() ?? false;
            _lastVolume = GetVolumeFromSystem() ?? 0f;

            // 使用轮询线程监听 HID 输入报告(监听物理按键)
            _hidPollingTimer = new System.Threading.Timer(_ =>
            {
                PollHidDevice();
            }, null, TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(50));
        }
    }

    /// <summary>
    /// 停止监听状态变化
    /// </summary>
    public void StopMonitoring()
    {
        ThrowIfDisposed();

        lock (_lock)
        {
            StopMonitoringInternal();
        }
    }

    /// <summary>
    /// 内部停止监听(不做 disposed 检查,供 Disconnect/Dispose 调用)
    /// </summary>
    private void StopMonitoringInternal()
    {
        if (!_isMonitoring)
            return;

        _isMonitoring = false;

        _hidPollingTimer?.Dispose();
        _hidPollingTimer = null;
    }

    /// <summary>
    /// 轮询 HID 设备读取输入报告
    /// </summary>
    private void PollHidDevice()
    {
        if (_disposed || !_isMonitoring || _hidDevice == null || !_hidDevice.IsConnected)
            return;

        try
        {
            // 非阻塞读取
            var report = _hidDevice.ReadReport(0); // 0 = 非阻塞

            if (report != null && report.Data != null && report.Data.Length > 0)
            {
                ParseHidReport(report.ReportId, report.Data);
            }
        }
        catch
        {
            // 忽略读取错误
        }
    }

    /// <summary>
    /// 解析 HID 报告,检测物理按键
    /// </summary>
    private void ParseHidReport(byte reportId, byte[] data)
    {
        // 忽略连接后短时间内的报告(可能是缓冲区残留数据)
        var timeSinceConnect = (DateTime.Now - _connectTime).TotalMilliseconds;
        if (timeSinceConnect < IgnoreReportsAfterConnectMs)
        {
            return;
        }

        // 使用配置中的按键报告 ID 和按键数据
        if (reportId == _config.ButtonReportId && data.Length >= 1 && data[0] == _config.ButtonPressData)
        {
            // 防抖:检查距离上次按键的时间
            var now = DateTime.Now;
            var elapsed = (now - _lastButtonPressTime).TotalMilliseconds;

            if (elapsed < ButtonDebounceMs)
            {
                // 忽略太快的重复按键
                return;
            }

            _lastButtonPressTime = now;

            // 硬件静音状态切换 - 维护本地状态
            _lastMuteState = !_lastMuteState;
            _lastVolume = GetVolumeFromSystem() ?? _lastVolume;

            try
            {
                // 触发事件,告知用户当前状态
                StateChanged?.Invoke(this, new AudioStateChangedEventArgs
                {
                    IsMuted = _lastMuteState,
                    Volume = _lastVolume,
                    Device = ConnectedDevice
                });

                // 触发物理按钮事件
                PhysicalButtonPressed?.Invoke(this, new PhysicalButtonEventArgs
                {
                    ButtonType = PhysicalButtonType.Mute,
                    IsPressed = true,
                    NewMuteState = _lastMuteState
                });
            }
            catch
            {
                // 忽略订阅者异常,避免影响轮询线程
            }
        }
    }

    public void Dispose()
    {
        // 保证只执行一次(线程安全)
        if (Interlocked.Exchange(ref _disposeSignaled, 1) != 0)
            return;

        lock (_lock)
        {
            if (_disposed)
                return;
            _disposed = true;
        }

        // 在锁外释放资源,避免潜在死锁/长时间占锁
        Disconnect();

        GC.SuppressFinalize(this);
    }
}

/// <summary>
/// HID 设备信息
/// </summary>
public class HidDeviceInfo
{
    public int VendorId { get; init; }
    public int ProductId { get; init; }
    public string? ProductName { get; init; }
    public string? DevicePath { get; init; }
    public ushort UsagePage { get; init; }
    public ushort Usage { get; init; }
    public int InputReportLength { get; init; }
    public int OutputReportLength { get; init; }
    public int FeatureReportLength { get; init; }

    public AudioDeviceInfo ToAudioDeviceInfo()
    {
        return new AudioDeviceInfo
        {
            Name = ProductName ?? $"HID Audio Device ({VendorId:X4}:{ProductId:X4})",
            DeviceId = DevicePath,
            VendorId = VendorId,
            ProductId = ProductId,
            ControllerType = AudioControllerType.HidAudio,
            SupportsMute = true,
            SupportsVolume = false
        };
    }
}
posted @ 2026-04-01 00:52  Timskt  阅读(1)  评论(0)    收藏  举报