
11 changed files with 1679 additions and 0 deletions
-
13XCoder/CrazyCoder.csproj
-
63XCoder/Properties/Resources.Designer.cs
-
120XCoder/Properties/Resources.resx
-
132XCoder/Windows/MelForm.Designer.cs
-
111XCoder/Windows/MelForm.cs
-
120XCoder/Windows/MelForm.resx
-
496XCoder/Windows/MultiChannelAudioProcessor.cs
-
248XCoder/Windows/UsbHelper.cs
-
111XCoder/Windows/UsbHubSelect.Designer.cs
-
145XCoder/Windows/UsbHubSelect.cs
-
120XCoder/Windows/UsbHubSelect.resx
@ -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; |
|||
} |
|||
} |
|||
} |
|||
} |
@ -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> |
@ -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; |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
|
|||
} |
|||
} |
@ -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> |
@ -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); |
|||
} |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
|||
} |
@ -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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue