Browse Source

USB HUB 设备列举。 音频梅尔图。

master
jiuhuan 5 days ago
parent
commit
aa970fcd26
  1. 13
      XCoder/CrazyCoder.csproj
  2. 63
      XCoder/Properties/Resources.Designer.cs
  3. 120
      XCoder/Properties/Resources.resx
  4. 132
      XCoder/Windows/MelForm.Designer.cs
  5. 111
      XCoder/Windows/MelForm.cs
  6. 120
      XCoder/Windows/MelForm.resx
  7. 496
      XCoder/Windows/MultiChannelAudioProcessor.cs
  8. 248
      XCoder/Windows/UsbHelper.cs
  9. 111
      XCoder/Windows/UsbHubSelect.Designer.cs
  10. 145
      XCoder/Windows/UsbHubSelect.cs
  11. 120
      XCoder/Windows/UsbHubSelect.resx

13
XCoder/CrazyCoder.csproj

@ -32,6 +32,11 @@
<None Remove="Protocols\**" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="XCom\SerialPortList.cs" />
<Compile Update="XNet\FrmApiDiscover.cs" />
</ItemGroup>
@ -62,6 +67,8 @@
<Content Include="数据库命名规范.txt" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
<PackageReference Include="NAudio" Version="2.2.1" />
<PackageReference Include="NewLife.Map" Version="2.6.2025.601" />
<PackageReference Include="NewLife.ModbusRTU" Version="2.0.2025.701" />
<PackageReference Include="NewLife.Remoting" Version="3.3.2025.701" />
@ -116,6 +123,12 @@
<EmbeddedResource Remove="NewModelForm\NewModel.resx" />
<EmbeddedResource Remove="XMessage\FrmMain.resx" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

63
XCoder/Properties/Resources.Designer.cs

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace CrazyCoder.Properties {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CrazyCoder.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

120
XCoder/Properties/Resources.resx

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

132
XCoder/Windows/MelForm.Designer.cs

@ -0,0 +1,132 @@
namespace XCoder
{
partial class MelForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
bt_Open = new Button();
cb_ch = new ComboBox();
pic_mel = new PictureBox();
pic_vol = new PictureBox();
label1 = new Label();
label2 = new Label();
((System.ComponentModel.ISupportInitialize)pic_mel).BeginInit();
((System.ComponentModel.ISupportInitialize)pic_vol).BeginInit();
SuspendLayout();
//
// bt_Open
//
bt_Open.Location = new Point(12, 12);
bt_Open.Name = "bt_Open";
bt_Open.Size = new Size(89, 25);
bt_Open.TabIndex = 0;
bt_Open.Text = "打开 wav";
bt_Open.UseVisualStyleBackColor = true;
bt_Open.Click += bt_Open_Click;
//
// cb_ch
//
cb_ch.DropDownStyle = ComboBoxStyle.DropDownList;
cb_ch.FormattingEnabled = true;
cb_ch.Location = new Point(135, 13);
cb_ch.Name = "cb_ch";
cb_ch.Size = new Size(121, 25);
cb_ch.TabIndex = 1;
cb_ch.SelectedIndexChanged += cb_ch_SelectedIndexChanged;
//
// pic_mel
//
pic_mel.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
pic_mel.BackColor = Color.LightGray;
pic_mel.Location = new Point(12, 71);
pic_mel.Name = "pic_mel";
pic_mel.Size = new Size(628, 80);
pic_mel.SizeMode = PictureBoxSizeMode.Zoom;
pic_mel.TabIndex = 2;
pic_mel.TabStop = false;
//
// pic_vol
//
pic_vol.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
pic_vol.BackColor = Color.LightGray;
pic_vol.Location = new Point(12, 186);
pic_vol.Name = "pic_vol";
pic_vol.Size = new Size(628, 200);
pic_vol.SizeMode = PictureBoxSizeMode.Zoom;
pic_vol.TabIndex = 3;
pic_vol.TabStop = false;
//
// label1
//
label1.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
label1.AutoSize = true;
label1.Location = new Point(12, 51);
label1.Name = "label1";
label1.Size = new Size(56, 17);
label1.TabIndex = 4;
label1.Text = "梅尔频谱";
//
// label2
//
label2.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
label2.AutoSize = true;
label2.Location = new Point(12, 166);
label2.Name = "label2";
label2.Size = new Size(32, 17);
label2.TabIndex = 5;
label2.Text = "音量";
//
// MelForm
//
AutoScaleDimensions = new SizeF(7F, 17F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(652, 404);
Controls.Add(label2);
Controls.Add(label1);
Controls.Add(pic_vol);
Controls.Add(pic_mel);
Controls.Add(cb_ch);
Controls.Add(bt_Open);
Name = "MelForm";
Text = "MelForm";
Load += MelForm_Load;
((System.ComponentModel.ISupportInitialize)pic_mel).EndInit();
((System.ComponentModel.ISupportInitialize)pic_vol).EndInit();
ResumeLayout(false);
PerformLayout();
}
#endregion
private Button bt_Open;
private ComboBox cb_ch;
private PictureBox pic_mel;
private PictureBox pic_vol;
private Label label1;
private Label label2;
}
}

111
XCoder/Windows/MelForm.cs

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace XCoder
{
[DisplayName("音频梅尔频谱")]
public partial class MelForm : Form, IXForm
{
public MelForm()
{
InitializeComponent();
}
private void MelForm_Load(object sender, EventArgs e)
{
}
List<Bitmap> _Mel = new List<Bitmap>();
List<Bitmap> _Vol = new List<Bitmap>();
Bitmap _MulVol = null;
private void bt_Open_Click(object sender, EventArgs e)
{
cb_ch.Items.Clear();
// cb_ch.DataSource = null;
_Mel = null;
_Vol = null;
var fm = new OpenFileDialog();
fm.ShowDialog();
var fileName = fm.FileName;
if (!fileName.EndsWith(".wav")) return;
// 生成多声道梅尔频谱图
var mulMel = new MultiChannelAudioProcessor(fileName);
_Mel = mulMel.GenerateMelSpectrograms();
_Vol = mulMel.GenerateVolumeCurve();
_MulVol = mulMel.GenerateVolumeCurveOverlay();
for (int i = 0; i < _Mel.Count; i++)
{
cb_ch.Items.Add($"声道{i + 1}");
}
cb_ch.SelectedIndex = 0;
}
private void cb_ch_SelectedIndexChanged(object sender, EventArgs e)
{
var idx = cb_ch.SelectedIndex;
if (_Mel == null) return;
if (_Vol == null) return;
pic_mel.Image = _Mel[idx];
pic_vol.Image = _Vol[idx];
}
protected override void WndProc(ref Message m)
{
const int WM_MOUSEWHEEL = 0x020A;
if (m.Msg == WM_MOUSEWHEEL)
{
// 在这里处理你的全局滚轮事件
short delta = 0;
if (IntPtr.Size == 8)
{
// 64位系统
delta = (short)(m.WParam.ToInt64() >> 16);
}
else
{
// 32位系统
delta = (short)(m.WParam.ToInt32() >> 16);
}
if (delta > 0)
{
// 向上滚动
if (cb_ch.SelectedIndex > 0)
{
cb_ch.SelectedIndex--;
}
}
else if (delta < 0)
{
// 向下滚动
if (cb_ch.SelectedIndex < cb_ch.Items.Count - 1)
{
cb_ch.SelectedIndex++;
}
}
// 如果你想阻止事件传递给其他控件,可以在这里返回
return;
}
base.WndProc(ref m);
}
}
}

120
XCoder/Windows/MelForm.resx

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

496
XCoder/Windows/MultiChannelAudioProcessor.cs

@ -0,0 +1,496 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using MathNet.Numerics;
using MathNet.Numerics.IntegralTransforms;
using NAudio.Wave;
namespace XCoder
{
public class MultiChannelAudioProcessor
{
// 梅尔频率转换参数
public int SampleRate = 44100; // 采样率
public int FftSize = 2048; // FFT窗口大小
public int HopSize = 512; // 帧移
public int MelBands = 80; // 梅尔带数量
public float MinFrequency = 100f; // 最小频率(Hz)
public float MaxFrequency = 8000f; // 最大频率(Hz)
/// <summary>多声道梅尔频谱列表(值)</summary>
public List<double[][]> MulMelSpectrum;
/// <summary>多声道音量曲线列表(db)</summary>
public List<double[]> MulVolumeCurve_Db;
public MultiChannelAudioProcessor(string audioFilePath)
{
// 1. 读取音频文件(多声道)
float[][] audioChannels = ReadAudioFile(audioFilePath);
var spectrograms = new List<Bitmap>();
// 初始化多声道梅尔频谱列表
MulMelSpectrum = new List<double[][]>(audioChannels.Length);
// 为每个声道生成频谱图
for (int channel = 0; channel < audioChannels.Length; channel++)
{
float[] audioData = audioChannels[channel];
// 2. 预处理音频(加窗)
int frames = (audioData.Length - FftSize) / HopSize + 1;
Complex[][] framesComplex = new Complex[frames][];
for (int i = 0; i < frames; i++)
{
// 提取帧
float[] frame = new float[FftSize];
Array.Copy(audioData, i * HopSize, frame, 0, FftSize);
// 应用汉宁窗
var window = Window.Hann(FftSize);
for (int j = 0; j < FftSize; j++)
{
frame[j] *= (float)window[j];
}
// 转换为复数
framesComplex[i] = frame.Select(x => new Complex(x, 0)).ToArray();
}
// 3. 计算FFT和功率谱
double[][] powerSpectrum = new double[frames][];
int spectrumSize = FftSize / 2 + 1;
for (int i = 0; i < frames; i++)
{
// 执行FFT
Fourier.Forward(framesComplex[i], FourierOptions.NoScaling);
// 计算功率谱(只取前半部分)
powerSpectrum[i] = new double[spectrumSize];
for (int j = 0; j < spectrumSize; j++)
{
powerSpectrum[i][j] = framesComplex[i][j].MagnitudeSquared();
}
}
// 4. 创建梅尔滤波器组
double[][] melFilterBank = CreateMelFilterBank();
// 5. 应用梅尔滤波器组
double[][] melSpectrum = new double[frames][];
for (int i = 0; i < frames; i++)
{
melSpectrum[i] = new double[MelBands];
for (int j = 0; j < MelBands; j++)
{
double sum = 0;
for (int k = 0; k < spectrumSize; k++)
{
sum += powerSpectrum[i][k] * melFilterBank[j][k];
}
melSpectrum[i][j] = sum;
}
}
MulMelSpectrum.Add(melSpectrum);
}
// 初始化多声道音量曲线列表
MulVolumeCurve_Db = new List<double[]>(audioChannels.Length);
// 为每个声道生成音量图
for (int channel = 0; channel < audioChannels.Length; channel++)
{
// 6. 转换为分贝每帧的音量
var melSpectrum = MulMelSpectrum[channel];
var volumeCurve = new double[melSpectrum.Length];
for (int i = 0; i < melSpectrum.Length; i++)
{
// 计算每帧的音量。 直接拿梅尔频谱的最大值作为音量
volumeCurve[i] = melSpectrum[i].Max();
}
for (int i = 0; i < volumeCurve.Length; i++)
{
if (volumeCurve[i] > 0)
{
// 转换为分贝
volumeCurve[i] = 10 * Math.Log10(volumeCurve[i] + double.Epsilon);
// 如果计算结果为NaN,设置为-100dB
// if (volumeCurve[i] == double.NaN) volumeCurve[i] = -100.0;
// 限制分贝值
volumeCurve[i] = Math.Max(volumeCurve[i], -100.0);
volumeCurve[i] = Math.Min(volumeCurve[i], 120.0);
}
else
{
// 如果音量为0,设置为-100dB
volumeCurve[i] = -100.0;
}
}
MulVolumeCurve_Db.Add(volumeCurve);
}
}
/// <summary>生成mel频谱图</summary>
/// <param name="hasTitle">启用图像抬头</param>
/// <returns></returns>
public List<Bitmap> GenerateMelSpectrograms(bool hasTitle = false)
{
var spectrograms = new List<Bitmap>();
// 为每个声道生成频谱图
for (int channel = 0; channel < MulMelSpectrum.Count; channel++)
{
// 6. 转换为分贝单位
double[][] melSpectrogramDb = ConvertToDecibels(MulMelSpectrum[channel]);
// 7. 创建图像
Bitmap spectrogramImage = CreateSpectrogramImage(melSpectrogramDb, $"Channel {channel + 1}", hasTitle);
spectrograms.Add(spectrogramImage);
}
return spectrograms;
}
/// <summary>生成音量曲线图</summary>
/// <param name="hasTitle">启用抬头</param>
/// <returns></returns>
public List<Bitmap> GenerateVolumeCurve(bool hasTitle = false)
{
var spectrograms = new List<Bitmap>();
// 为每个声道生成音量图
for (int channel = 0; channel < MulVolumeCurve_Db.Count; channel++)
{
// 7. 创建图像
Bitmap spectrogramImage = CreateVolumeCurveImage(MulVolumeCurve_Db[channel], $"Channel {channel + 1}");
spectrograms.Add(spectrogramImage);
}
return spectrograms;
}
/// <summary>生成音量曲线图,多通道在一张图内</summary>
/// <returns></returns>
public Bitmap GenerateVolumeCurveOverlay()
{
int width = MulVolumeCurve_Db[0].Length;
int height = 200 * MulVolumeCurve_Db.Count;
var hasTitle = false;
// 增加高度以容纳标题
int titleHeight = 30;
if (!hasTitle) titleHeight = 0;
Bitmap image = new Bitmap(width, height + titleHeight, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(image))
{
// 绘制黑色背景
g.Clear(Color.LightGray);
/*
// 绘制标题
if (hasTitle)
{
using (Font font = new Font("Arial", 12))
using (Brush brush = new SolidBrush(Color.White))
{
g.DrawString(channelName, font, brush, 10, 5);
}
}
*/
}
double min = -100.5;
double max = 120.5;
var cls = new Color[]
{
Color.Red,
Color.Green,
Color.Yellow,
Color.Orange,
Color.Blue,
Color.Cyan,
Color.Magenta,
};
for (int ch = 0; ch < MulVolumeCurve_Db.Count; ch++)
{
// 每个声道的音量曲线
var volumes = MulVolumeCurve_Db[ch];
var color = cls[ch % cls.Length];
for (int x = 0; x < width; x++)
{
int y = (int)((volumes[x] - min) / (max - min) * height);
// 注意: 图像坐标系y轴向下,频谱y轴向上,所以需要翻转
// 并且向下偏移titleHeight像素
image.SetPixel(x, height - 1 - y + titleHeight, color);
}
}
return image;
}
private float[][] ReadAudioFile(string filePath)
{
using (var audioFile = new AudioFileReader(filePath))
{
// 获取参数
int channelCount = audioFile.WaveFormat.Channels;
int sampleRate = audioFile.WaveFormat.SampleRate;
SampleRate = sampleRate;
// 为每个声道创建缓冲区
float[][] channelBuffers = new float[channelCount][];
for (int i = 0; i < channelCount; i++)
{
channelBuffers[i] = new float[audioFile.Length / (4 * channelCount)]; // 每个float占4字节
}
// 临时缓冲区用于读取
float[] interleavedBuffer = new float[audioFile.Length / 4];
int samplesRead = audioFile.Read(interleavedBuffer, 0, interleavedBuffer.Length);
// 解交错音频数据(多声道交错存储)
for (int i = 0; i < samplesRead; i++)
{
int channel = i % channelCount;
int position = i / channelCount;
if (position < channelBuffers[channel].Length)
{
channelBuffers[channel][position] = interleavedBuffer[i];
}
}
return channelBuffers;
}
}
private double[][] CreateMelFilterBank()
{
double[][] filterBank = new double[MelBands][];
int spectrumSize = FftSize / 2 + 1;
// 计算梅尔频率范围
double minMel = HzToMel(MinFrequency);
double maxMel = HzToMel(MaxFrequency);
// 创建梅尔点
double[] melPoints = new double[MelBands + 2];
for (int i = 0; i < MelBands + 2; i++)
{
melPoints[i] = minMel + i * (maxMel - minMel) / (MelBands + 1);
}
// 将梅尔点转换回Hz
double[] hzPoints = melPoints.Select(MelToHz).ToArray();
// 将Hz点转换为FFT bin索引
int[] binIndices = hzPoints.Select(hz => (int)Math.Floor((FftSize + 1) * hz / SampleRate)).ToArray();
// 创建三角形滤波器
for (int i = 0; i < MelBands; i++)
{
filterBank[i] = new double[spectrumSize];
int start = binIndices[i];
int peak = binIndices[i + 1];
int end = binIndices[i + 2];
// 上升斜坡
for (int j = start; j <= peak; j++)
{
if (j >= 0 && j < spectrumSize)
{
filterBank[i][j] = (j - start) / (double)(peak - start);
}
}
// 下降斜坡
for (int j = peak; j <= end; j++)
{
if (j >= 0 && j < spectrumSize)
{
filterBank[i][j] = 1 - (j - peak) / (double)(end - peak);
}
}
}
return filterBank;
}
private static double HzToMel(double hz)
{
return 2595 * Math.Log10(1 + hz / 700.0);
}
private static double MelToHz(double mel)
{
return 700 * (Math.Pow(10, mel / 2595.0) - 1);
}
private static double[][] ConvertToDecibels(double[][] melSpectrum)
{
double minDb = -100; // 最小分贝值
for (int i = 0; i < melSpectrum.Length; i++)
{
for (int j = 0; j < melSpectrum[i].Length; j++)
{
// 计算分贝值,并限制最小值
double db = 10 * Math.Log10(melSpectrum[i][j] + double.Epsilon);
melSpectrum[i][j] = Math.Max(db, minDb);
}
}
return melSpectrum;
}
private Bitmap CreateSpectrogramImage(double[][] melSpectrogramDb, string channelName, bool hasTitle = false)
{
int width = melSpectrogramDb.Length;
int height = melSpectrogramDb[0].Length;
// 增加高度以容纳标题
int titleHeight = 30;
if (!hasTitle) titleHeight = 0;
Bitmap image = new Bitmap(width, height + titleHeight, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(image))
{
// 绘制黑色背景
g.Clear(Color.Black);
// 绘制标题
if (hasTitle)
{
using (Font font = new Font("Arial", 12))
using (Brush brush = new SolidBrush(Color.White))
{
g.DrawString(channelName, font, brush, 10, 5);
}
}
}
// 找到最小和最大值用于归一化
double min = melSpectrogramDb.Min(frame => frame.Min());
double max = melSpectrogramDb.Max(frame => frame.Max());
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
// 归一化到0-1范围
double normalized = (melSpectrogramDb[x][y] - min) / (max - min);
// 使用热图颜色方案
Color color = HeatMapColor(normalized);
// 注意: 图像坐标系y轴向下,频谱y轴向上,所以需要翻转
// 并且向下偏移titleHeight像素
image.SetPixel(x, height - 1 - y + titleHeight, color);
}
}
return image;
}
private Bitmap CreateVolumeCurveImage(double[] volumes, string channelName, bool hasTitle = false)
{
int width = volumes.Length;
int height = 200;
// 增加高度以容纳标题
int titleHeight = 30;
if (!hasTitle) titleHeight = 0;
Bitmap image = new Bitmap(width, height + titleHeight, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(image))
{
// 绘制黑色背景
g.Clear(Color.LightGray);
// 绘制标题
if (hasTitle)
{
using (Font font = new Font("Arial", 12))
using (Brush brush = new SolidBrush(Color.White))
{
g.DrawString(channelName, font, brush, 10, 5);
}
}
}
double min = -100.5;
double max = 120.5;
for (int x = 0; x < width; x++)
{
int y = (int)((volumes[x] - min) / (max - min) * height);
// 注意: 图像坐标系y轴向下,频谱y轴向上,所以需要翻转
// 并且向下偏移titleHeight像素
image.SetPixel(x, height - 1 - y + titleHeight, Color.Red);
}
return image;
}
private static Color HeatMapColor(double value)
{
// 简单的热图颜色方案
// 蓝色(冷) -> 青色 -> 绿色 -> 黄色 -> 红色(热)
int r, g, b;
if (value < 0.25)
{
// 蓝色到青色
r = 0;
g = (int)(4 * 255 * value);
b = 255;
}
else if (value < 0.5)
{
// 青色到绿色
r = 0;
g = 255;
b = (int)(255 - 4 * 255 * (value - 0.25));
}
else if (value < 0.75)
{
// 绿色到黄色
r = (int)(4 * 255 * (value - 0.5));
g = 255;
b = 0;
}
else
{
// 黄色到红色
r = 255;
g = (int)(255 - 4 * 255 * (value - 0.75));
b = 0;
}
return Color.FromArgb(r, g, b);
}
}
}

248
XCoder/Windows/UsbHelper.cs

@ -0,0 +1,248 @@

using Microsoft.Win32;
using NewLife;
using NewLife.Log;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Text;
namespace XCoder
{
/// <summary>USB复合设备</summary>
public class UsbSubDev
{
/// <summary>MI_00, MI_01, MI_02</summary>
public int MI { get; set; }
/// <summary>名称</summary>
public string Name { get; set; }
public override string ToString()
{
return $"[{MI}] {Name}";
}
}
public class UsbDevice
{
/// <summary>端口号</summary>
public int Port { get; set; }
/// <summary>设备名称</summary>
public string Name { get; set; }
/// <summary>容器ID,USB设备唯一ID,复合设备中子设备共有(相同)</summary>
public string ContainerId { get; set; }
/// <summary>PNPDeviceID,USB设备注册表路径,复合设备中子设备加 &MI_xx</summary>
public string PNPDeviceID { get; set; }
public string VID { get; set; }
public string PID { get; set; }
/// <summary>复合设备,设备名称</summary>
public List<UsbSubDev> SubDev { get; set; }
public override string ToString()
{
var sb = new StringBuilder();
sb.Append($"{Port,2} VID_{VID} PID_{PID} {ContainerId} ");
// sb.Append($"{CurrentIdPrefix,16} ");
// sb.Append($"{ParentIdPrefix,16} ");
if (SubDev != null && SubDev.Count > 0)
{
sb.Append(string.Join(" + ", SubDev));
}
else
{
sb.Append($"{Name}");
}
return sb.ToString();
}
}
public class UsbHelper
{
/// <summary>获取USB集线集信息</summary>
/// <remarks>集线集编号 & 设备信息</remarks>
/// <returns></returns>
public static Dictionary<int, List<UsbDevice>> GetAllUsbInfo()
{
var roots = new Dictionary<int, List<UsbDevice>>();
// 查询所有 USB 设备
using (var searcher = new ManagementObjectSearcher(@"SELECT * FROM Win32_PnPEntity WHERE PNPDeviceID LIKE 'USB%'"))
{
var devices = searcher.Get();
foreach (var device in devices)
{
string name = device["Name"]?.ToString();
string pnpId = device["PNPDeviceID"]?.ToString();
// XTrace.WriteLine($"设备名称: {name}");
// XTrace.WriteLine($"PNPDeviceID: {pnpId}");
// 复合设备中的子设备
if (pnpId.IndexOf("&MI_") >= 0) continue;
// 获取注册表中的 LocationInformation
string location = GetLocationInformation(pnpId);
if (location == null) continue;
// XTrace.WriteLine($"位置信息: {location}");
// XTrace.WriteLine(new string('-', 50));
var phead = "Port_#";
var hhead = "Hub_#";
if (location.IndexOf(phead) < 0) continue;
var strs = location.Split('.');
var port = strs[0].TrimStart(phead).ToInt();
var hub = strs[1].TrimStart(hhead).ToInt();
var cid = GetContainerId(pnpId);
var (vid, pid) = GetVidPid(pnpId);
var dev = new UsbDevice
{
Port = port,
Name = name,
ContainerId = cid,
VID = vid,
PID = pid,
PNPDeviceID = pnpId,
// CurrentIdPrefix = pnpId.Split('\\').LastOrDefault(),
// ParentIdPrefix = GetParentIdPrefix(pnpId),
};
if (roots.ContainsKey(hub)) { roots[hub].Add(dev); }
else { roots[hub] = new List<UsbDevice> { dev }; }
}
// 处理复合设备
foreach (var device in devices)
{
string name = device["Name"]?.ToString();
string pnpId = device["PNPDeviceID"]?.ToString();
// XTrace.WriteLine($"设备名称: {name}");
// XTrace.WriteLine($"PNPDeviceID: {pnpId}");
// 复合设备中的子设备
if (pnpId.IndexOf("&MI_") < 0) continue;
var mi = -1;
var miStr = pnpId.Split('&', '\\').FirstOrDefault(s => s.StartsWith("MI_"));
mi = miStr != null ? miStr.TrimStart("MI_").ToInt() : -1;
// 容器ID
var cid = GetContainerId(pnpId);
// 找到对应的物理设备
UsbDevice dev = null;
foreach (var kv in roots)
{
// 查找对应的集线集
var usbDev = kv.Value.FirstOrDefault(d => d.ContainerId == cid);
if (usbDev != null)
{
dev = usbDev;
break;
}
}
if (dev == null) continue;
// 复合设备的子设备名称
if (dev.SubDev == null) dev.SubDev = new List<UsbSubDev>();
dev.SubDev.Add(new UsbSubDev { MI = mi, Name = name });
}
}
// 整理数据,友好输出
foreach (var kv in roots)
{
// 按端口号排序
if (kv.Value == null || kv.Value.Count > 1)
{
kv.Value.Sort((x, y) => x.Port.CompareTo(y.Port));
}
foreach (var dev in kv.Value)
{
// 按复合设备的MI排序
if (dev.SubDev != null && dev.SubDev.Count > 1)
{
dev.SubDev.Sort((x, y) => x.MI.CompareTo(y.MI));
}
}
}
return roots;
}
/// <summary>打印USB设备</summary>
/// <param name="usbInfo"></param>
/// <returns></returns>
public static string UsbInfoString(Dictionary<int, List<UsbDevice>> usbInfo)
{
var sb = new StringBuilder();
foreach (var kv in usbInfo)
{
sb.AppendLine("");
sb.AppendLine($"-- Hub_{kv.Key} --------------------");
foreach (var dev in kv.Value)
{
sb.AppendLine(dev.ToString());
}
}
return sb.ToString();
}
// 从注册表中读取 LocationInformation
static string GetLocationInformation(string devInstPath)
{
if (string.IsNullOrEmpty(devInstPath)) return null;
using (var key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Enum\" + devInstPath))
{
return key?.GetValue("LocationInformation") as string;
}
}
// 从注册表中读取 ContainerId
public static string GetContainerId(string devInstPath)
{
if (string.IsNullOrEmpty(devInstPath)) return null;
using (var key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Enum\" + devInstPath))
{
return key?.GetValue("ContainerID") as string;
}
}
/// <summary>提取VID PID</summary>
/// <param name="pnpId"></param>
/// <returns></returns>
static (string, string) GetVidPid(string pnpId)
{
if (pnpId.IsNullOrEmpty()) return ("", "");
// "USB\\VID_0408&PID_5365\\7&2C27FCE3&0&0000"
// "USB\\VID_0408&PID_5365&MI_00\\7&2C27FCE3&0&0000"
var strs = pnpId.Split('\\');
if (strs.Length < 3) return ("", "");
// 取第二段,包含 VID 和 PID
// VID_0408&PID_5365
// VID_0408&PID_5365&MI_00
var vpid = strs[1];
if (vpid.IsNullOrEmpty()) return ("", "");
strs = vpid.Split('&');
if (strs.Length < 2) return ("", "");
var vid = strs[0].Split('_').LastOrDefault();
var pid = strs[1].Split('_').LastOrDefault();
return (vid, pid);
}
}
}

111
XCoder/Windows/UsbHubSelect.Designer.cs

@ -0,0 +1,111 @@
namespace XCoder
{
partial class UsbHubSelect
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
cb_Hub = new ComboBox();
rtb_Show = new RichTextBox();
bt_Comf = new Button();
bt_Canel = new Button();
label1 = new Label();
SuspendLayout();
//
// cb_Hub
//
cb_Hub.DropDownStyle = ComboBoxStyle.DropDownList;
cb_Hub.FormattingEnabled = true;
cb_Hub.Location = new Point(52, 12);
cb_Hub.Name = "cb_Hub";
cb_Hub.Size = new Size(113, 25);
cb_Hub.TabIndex = 0;
//
// rtb_Show
//
rtb_Show.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
rtb_Show.Font = new Font("Consolas", 9F, FontStyle.Regular, GraphicsUnit.Point, 0);
rtb_Show.Location = new Point(12, 43);
rtb_Show.Name = "rtb_Show";
rtb_Show.Size = new Size(889, 327);
rtb_Show.TabIndex = 1;
rtb_Show.Text = "";
//
// bt_Comf
//
bt_Comf.Location = new Point(217, 12);
bt_Comf.Name = "bt_Comf";
bt_Comf.Size = new Size(75, 25);
bt_Comf.TabIndex = 2;
bt_Comf.Text = "确认";
bt_Comf.UseVisualStyleBackColor = true;
bt_Comf.Click += bt_Comf_Click;
//
// bt_Canel
//
bt_Canel.Location = new Point(310, 11);
bt_Canel.Name = "bt_Canel";
bt_Canel.Size = new Size(75, 25);
bt_Canel.TabIndex = 3;
bt_Canel.Text = "取消";
bt_Canel.UseVisualStyleBackColor = true;
bt_Canel.Click += bt_Canel_Click;
//
// label1
//
label1.AutoSize = true;
label1.Location = new Point(12, 15);
label1.Name = "label1";
label1.Size = new Size(34, 17);
label1.TabIndex = 4;
label1.Text = "HUB";
//
// UsbHubSelect
//
AutoScaleDimensions = new SizeF(7F, 17F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(913, 382);
Controls.Add(label1);
Controls.Add(bt_Canel);
Controls.Add(bt_Comf);
Controls.Add(rtb_Show);
Controls.Add(cb_Hub);
Name = "UsbHubSelect";
Text = "UsbHubSelect";
Load += UsbHubSelect_Load;
ResumeLayout(false);
PerformLayout();
}
#endregion
private ComboBox cb_Hub;
private RichTextBox rtb_Show;
private Button bt_Comf;
private Button bt_Canel;
private Label label1;
}
}

145
XCoder/Windows/UsbHubSelect.cs

@ -0,0 +1,145 @@

using NewLife;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static XCoder.UsbHelper;
namespace XCoder
{
[DisplayName("USB HUB 设备")]
public partial class UsbHubSelect : Form, IXForm
{
public UsbHubSelect()
{
InitializeComponent();
}
/// <summary>HUB号 & HUB下所有信息</summary>
public Dictionary<int, List<UsbDevice>> Hubs;
/// <summary>选中的 Hub 编号。 负数无效</summary>
public int SelectedHub { get; private set; } = -1;
private void UsbHubSelect_Load(object sender, EventArgs e)
{
rtb_Show.MouseWheel += Rtb_Show_MouseWheel;
Hubs = UsbHelper.GetAllUsbInfo();
foreach (var kv in Hubs)
{
var hub = kv.Key;
var devs = kv.Value;
cb_Hub.Items.Add($"{hub}");
}
cb_Hub.SelectedValueChanged += Cb_Hub_SelectedValueChanged;
if (cb_Hub.Items.Count > 0) cb_Hub.SelectedIndex = 0;
}
private void Rtb_Show_MouseWheel(object sender, MouseEventArgs e)
{
if (e.Delta > 0)
{
// 向上滚动
if (cb_Hub.SelectedIndex > 0)
{
cb_Hub.SelectedIndex--;
}
}
else if (e.Delta < 0)
{
// 向下滚动
if (cb_Hub.SelectedIndex < cb_Hub.Items.Count - 1)
{
cb_Hub.SelectedIndex++;
}
}
}
private void Cb_Hub_SelectedValueChanged(object sender, EventArgs e)
{
rtb_Show.Clear();
var hub = cb_Hub.Text.ToInt(-1);
if (hub < 0) return;
var devs = Hubs[hub];
var sb = new StringBuilder();
foreach (var dev in devs)
{
sb.AppendLine(dev.ToString());
}
rtb_Show.Text = sb.ToString();
}
private void bt_Comf_Click(object sender, EventArgs e)
{
SelectedHub = cb_Hub.Text.ToInt(-1);
this.Close();
}
private void bt_Canel_Click(object sender, EventArgs e)
{
SelectedHub = -1;
this.Close();
}
protected override void WndProc(ref Message m)
{
const int WM_MOUSEWHEEL = 0x020A;
if (m.Msg == WM_MOUSEWHEEL)
{
// 在这里处理你的全局滚轮事件
short delta = 0;
if(IntPtr.Size == 8)
{
// 64位系统
delta = (short)(m.WParam.ToInt64() >> 16);
}
else
{
// 32位系统
delta = (short)(m.WParam.ToInt32() >> 16);
}
// int delta = (short)(m.WParam.ToInt32() >> 16);
// Console.WriteLine("Mouse wheel moved: " + delta);
if (delta > 0)
{
// 向上滚动
if (cb_Hub.SelectedIndex > 0)
{
cb_Hub.SelectedIndex--;
}
}
else if (delta < 0)
{
// 向下滚动
if (cb_Hub.SelectedIndex < cb_Hub.Items.Count - 1)
{
cb_Hub.SelectedIndex++;
}
}
// 如果你想阻止事件传递给其他控件,可以在这里返回
return;
}
base.WndProc(ref m);
}
}
}

120
XCoder/Windows/UsbHubSelect.resx

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
Loading…
Cancel
Save