C# WinForms Chart水平网格单元格着色:将音频频率图表改为钢琴卷帘样式
把WinForms Chart改成钢琴卷帘风格的实现方案
当然可以!把普通的音频频率图表改成钢琴卷帘样式,核心就是贴合钢琴卷帘的视觉逻辑——垂直方向对应音高(频率)、水平方向对应时间帧,用黑白键分区+高亮有数据的音块来提升可读性。下面是具体的实现步骤:
1. 调整Chart基础布局,对齐钢琴卷帘逻辑
首先把Chart的基础配置改成适合钢琴卷帘的模式:
- 将Series的
ChartType设为Column(柱状图),并把PointWidth设为1,让柱子紧密填满每个X轴单元格,形成连续的块:chart1.Series[0].ChartType = SeriesChartType.Column; chart1.Series[0]["PointWidth"] = "1"; - Y轴用对数刻度(因为钢琴音高是按八度对数分布的),手动设置范围覆盖88键钢琴的频率区间(27.5Hz到4186Hz):
var chartArea = chart1.ChartAreas[0]; chartArea.AxisY.IsLogarithmic = true; chartArea.AxisY.Minimum = 27.5; // 最低音A0 chartArea.AxisY.Maximum = 4186; // 最高音C8 chartArea.AxisY.MajorGrid.Enabled = false; // 隐藏网格线,更像钢琴键 - X轴对应音频帧/时间,隐藏多余的刻度和标签(如果不需要显示具体帧号):
chartArea.AxisX.LabelStyle.Enabled = false; chartArea.AxisX.MajorTickMark.Enabled = false;
2. 绘制钢琴黑白键背景(关键步骤)
钢琴卷帘的核心视觉就是黑白键分区,我们可以通过Chart的Paint事件自定义绘制背景:
private void chart1_Paint(object sender, PaintEventArgs e) { var chartArea = chart1.ChartAreas[0]; // 提前预存88键钢琴的频率数组(可以用公式生成,比如A0=27.5Hz,每个半音乘2^(1/12)) double[] pianoKeys = GeneratePianoFrequencies(); foreach (double freq in pianoKeys) { // 把频率值转换为Chart控件上的Y坐标 float yPixel = (float)chartArea.AxisY.ValueToPixelPosition(freq); // 计算当前键的高度(取下一个键的Y坐标差) int keyIndex = Array.IndexOf(pianoKeys, freq); float nextY = keyIndex < pianoKeys.Length - 1 ? (float)chartArea.AxisY.ValueToPixelPosition(pianoKeys[keyIndex + 1]) : (float)chartArea.Position.Y; float keyHeight = yPixel - nextY; // 判断当前键是黑键还是白键(根据音名规则:C#/D#/F#/G#/A#是黑键) bool isBlackKey = CheckIfBlackKey(freq); // 绘制键的背景 Brush keyBrush = isBlackKey ? Brushes.DimGray : Brushes.LightGray; e.Graphics.FillRectangle(keyBrush, chartArea.Position.X, nextY, chartArea.Position.Width, keyHeight); } }
需要两个辅助方法:
GeneratePianoFrequencies():用公式生成88键的频率数组(A0=27.5,每个半音频率=前一个×2^(1/12))CheckIfBlackKey():根据频率计算对应的音名,判断是否为黑键
3. 绑定音频频率数据,高亮激活的音块
把音频的每帧频率数据映射到对应的钢琴键,然后添加到Chart的Series中:
// 假设audioFrames是你的音频帧数据,每个帧包含多个检测到的频率 for (int frameIndex = 0; frameIndex < audioFrames.Length; frameIndex++) { var detectedFrequencies = audioFrames[frameIndex]; foreach (double freq in detectedFrequencies) { // 找到最接近的钢琴键频率 double closestKey = FindClosestPianoKey(freq, pianoKeys); int keyIndex = Array.IndexOf(pianoKeys, closestKey); // 添加数据点,设置高亮颜色 chart1.Series[0].Points.AddXY(frameIndex, keyIndex); chart1.Series[0].Points.Last().Color = Color.DodgerBlue; // 高亮激活的音 chart1.Series[0].Points.Last().BorderWidth = 0; // 去掉柱子边框 } }
FindClosestPianoKey()方法负责把检测到的频率匹配到最接近的钢琴键频率,避免数据点错位。
4. 优化交互和细节
- 启用缩放和滚动,方便查看长音频:
chartArea.CursorX.IsUserEnabled = true; chartArea.CursorX.IsUserSelectionEnabled = true; chartArea.AxisX.ScaleView.Zoomable = true; chartArea.AxisX.ScrollBar.IsPositionedInside = true; - 添加ToolTip,鼠标悬停时显示音名和频率:
private void chart1_MouseMove(object sender, MouseEventArgs e) { var hitResult = chart1.HitTest(e.X, e.Y); if (hitResult.PointIndex >= 0) { var point = hitResult.Series.Points[hitResult.PointIndex]; int keyIndex = (int)point.YValues[0]; double freq = pianoKeys[keyIndex]; string noteName = GetNoteName(freq); // 转换频率为音名(比如C4) toolTip1.SetToolTip(chart1, $"{noteName} | {freq:F1} Hz"); } }
这样调整后,你的Chart就会完全接近钢琴卷帘的样式,可读性会大大提升!
内容的提问来源于stack exchange,提问作者porras
相关产品推荐
相关产品推荐

