说明:
自制用GDI+绘制环形频谱。
效果截图:
1.播放前。
2.播放时。
思路讲解:
整个程序是两个窗体,一个下层窗体负责绘制频谱,一个上层窗体负责播放控制等。
-->上层窗体是用的LayeredSkin界面库,因为要窗体判断鼠标位置实现淡入淡出效果,而一般窗体我不知道该怎么实现,只好用别人的。
这个因为是用的别人的现成的,我就不多叙述了
-->下层窗体就是一般的Form窗体,使用UpdateLayeredWindow函数绘制透明窗体,
[C#] 纯文本查看 复制代码 public void SetBits(Bitmap bitmap)//调用UpdateLayeredWindow()方法。
{
if (!Bitmap.IsCanonicalPixelFormat(bitmap.PixelFormat) || !Bitmap.IsAlphaPixelFormat(bitmap.PixelFormat))
throw new ApplicationException("图片必须是32位带Alhpa通道的图片。");
...
//发帖字数限制就不详细贴代码了,源码里有
}
因为需要一张带透明通道的图片,我没有,但是可以创建一个
[C#] 纯文本查看 复制代码 private Bitmap GetBitmap()
{
Bitmap bmp = new Bitmap(this.Width, this.Height);//创建位图
Graphics g = Graphics.FromImage(bmp);//创建画布
SolidBrush sb = new SolidBrush(Color.FromArgb(0, 0, 0, 0));//创建一个带透明通道的笔刷,改变Alpha值可以调节窗体的透明度
Rectangle r = new Rectangle(0, 0, this.Width, this.Height);
g.FillRectangle(sb, r);//用这个笔刷填充整个位图,达到创建透明位图的效果
return bmp;
}
然后在窗体加载事件中直接调用SetBits()函数就能让窗体任意透明了
[C#] 纯文本查看 复制代码 Bitmap bmp = GetBitmap();
SetBits(bmp);
部分代码:
接下来就是在这张带透明通道的位图上绘制频谱了(频谱数据利用bass音频处理库获取)
有点乱,我也没怎么整理,就将就吧
[C#] 纯文本查看 复制代码
#region 谱线的字段及属性
Pen p;//内圈画笔
Pen p2;//外圈画笔
private float _DeviationAngle = 0;//偏移角度
double offsetAngle = (double)360 / 90;//谱线间相隔角度(180/45)
private PointF _CenterPointF;//频谱中心点
private int _LineNum = 45;//谱线数目(一半,为了对称好看)
private int _LineWidth = 3;//线宽
private int _LineAlpha = 50;//线透明度
private int _LineStopHeight = 5;//谱线平常高度
private int _LineHeight = 0;//数据填充时谱线高度
private int _LineR = 100;//谱线的RGB值
private int _LineG = 100;
private int _LineB = 100;
private int _Speed = 100;//刷新频率
private float[] fft;
private int[] _LineAlphas = new int[45];//谱线的渐变透明度
private int[] _OldH = new int[45];//外圈谱线上一次高度
private int[] _OldH2 = new int[45];//内圈谱线上一次高度
private string _NewTime = "00:00";//已播放的时间
private string _EndTime = "00:00";//播放总时间
private string _Title = "听音乐";//歌曲名
private string _Artist = "心灵之音";//作家
private int _FontAlpha = 200;//字体透明度
#endregion
#region 绘制频谱
private void _Timer1_Tick(object sender, EventArgs e)
{
p = new Pen(Color.FromArgb(this._LineAlpha, this._LineG, this._LineR, this._LineB), _LineWidth);
p2 = new Pen(Color.FromArgb(this._LineAlpha, this._LineG, this._LineR, this._LineB), _LineWidth + 1);
_CenterPointF = new PointF(this.Width / 2, this.Height / 2);
using (bmp = new Bitmap(this.Width, this.Height))
{
g = Graphics.FromImage(bmp);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.FillRectangle(sb, r);
int _LineHeight1 = 0;
int _LineHeight2 = 0;
for (int i = 0; i < _LineNum; i++)
{
if (fft == null)
{
g.DrawLine(p2, GetPointF(_CenterPointF, 140, offsetAngle * i + _DeviationAngle), GetPointF(_CenterPointF, 140 + _LineStopHeight, offsetAngle * i + _DeviationAngle));
g.DrawLine(p2, GetPointF(_CenterPointF, 140, 360 - offsetAngle * (i + 1) + _DeviationAngle), GetPointF(_CenterPointF, 140 + _LineStopHeight, 360 - offsetAngle * (i + 1) + _DeviationAngle));
g.DrawLine(p, GetPointF(_CenterPointF, 136, offsetAngle * i - _DeviationAngle), GetPointF(_CenterPointF, 136, offsetAngle * i - _DeviationAngle));
g.DrawLine(p, GetPointF(_CenterPointF, 136, 360 - offsetAngle * (i + 1) - _DeviationAngle), GetPointF(_CenterPointF, 136, 360 - offsetAngle * (i + 1) - _DeviationAngle));
}
else
{
_LineHeight = (int)(fft* 6000 * 5000);
#region 外圈
_LineHeight1 = (int)Math.Pow(_LineHeight, 1 / 3f);//减小谱线间的差距
if (_LineHeight1 <= 45)
{
_LineHeight1 = _LineStopHeight;
}
else if (_LineHeight1 >= 170)
{
_LineHeight1 = 170;
}
if (_OldH > _LineHeight1)
{
_OldH -= 7;//回落速度
if (_LineAlphas > _LineAlpha)
{
_LineAlphas -= 18;
}
else { _LineAlphas = _LineAlpha; }
}
else
{
_OldH = _LineHeight1;
_LineAlphas = 255;
}
if (_OldH <= _LineStopHeight)
{
_OldH = _LineStopHeight;
_LineAlphas = _LineAlpha;
}
#endregion
#region 内圈
//同理
#endregion
p2.Color = Color.FromArgb(_LineAlphas, this._LineG, this._LineR, this._LineB);
p.Color = Color.FromArgb(_LineAlphas, this._LineG, this._LineR, this._LineB);
//为了频谱对称好看,所以采取对半绘制
g.DrawLine(p2, GetPointF(_CenterPointF, 140, offsetAngle * i + _DeviationAngle), GetPointF(_CenterPointF, 140 + _OldH, offsetAngle * i + _DeviationAngle));
g.DrawLine(p2, GetPointF(_CenterPointF, 140, 360 - offsetAngle * (i + 1) + _DeviationAngle), GetPointF(_CenterPointF, 140 + _OldH, 360 - offsetAngle * (i + 1) + _DeviationAngle));
g.DrawLine(p, GetPointF(_CenterPointF, 136, offsetAngle * i - _DeviationAngle), GetPointF(_CenterPointF, 136 - _OldH2, offsetAngle * i - _DeviationAngle));
g.DrawLine(p, GetPointF(_CenterPointF, 136, 360 - offsetAngle * (i + 1) - _DeviationAngle), GetPointF(_CenterPointF, 136 - _OldH2, 360 - offsetAngle * (i + 1) - _DeviationAngle));
}
}
StringFormat sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
g.DrawString(_NewTime + "\n" + _EndTime, new Font("李旭科书法 v1.4", 50, FontStyle.Bold), new SolidBrush(Color.FromArgb(this._FontAlpha, this._LineG, this._LineR, this._LineB)), this.ClientRectangle, sf);
g.DrawString(_Title + "\n" + _Artist, new Font("宋体", 15, FontStyle.Bold), new SolidBrush(Color.FromArgb(this._FontAlpha, this._LineG, this._LineR, this._LineB)), this.ClientRectangle, sf);
_DeviationAngle = _DeviationAngle >= 360 ? 0 : _DeviationAngle + 0.4f;
SetBits(bmp);
bmp.Dispose();
g.Dispose();
}
p.Dispose();
p2.Dispose();
}
/// <summary>
/// 根据中心点 半径 和角度,获取从中心点出发的线段终点
/// </summary>
private PointF GetPointF(PointF centerPointF, int r, double angle)
{
double A = Math.PI * angle / 180;//(angle/360)*2PI
float xF = centerPointF.X + r * (float)Math.Cos(A);
float yF = centerPointF.Y + r * (float)Math.Sin(A);
return (new PointF(xF, yF));
}
#endregion
编后语:
- 绘制部分就搞定了,但是有个很严重的bug,就是随着程序的运行,系统会越来越卡,直到一定时间后程序自动崩溃或者手动退出,系统才会恢复原样
- 播放列表还没绘制,接触编程的时间还是太短了,很多东西不了解,没能力优化,把它分享出来其实主要是想请大牛们帮忙看看,是不是哪里出问题了。帮忙优化一下。
- 还有就是希望大家帮忙提点意见,可以在什么地方改进一下。小弟不胜感激
源代码和demo下载:
字体下载:
|
评分
-
查看全部评分
|