最近想把Win32 GDI绘图捡回来一些。在CFree下写了一个读取16-bit int PCM wav文件并在窗口中绘制波形的小程序。总结一下主要的收获:
1. Windows资源文件.rc
以前在Visual Studio下编写Win32和MFC程序,rc文件都是自动生成。这次用CFree写程序,条件不允许,就需要自己编辑了。这次主要用到rc文件添加菜单。
IDC_WIN32WNDEMO MENU
BEGIN
POPUP "文件(&F)"
BEGIN
MENUITEM "打开(&O)" IDM_OPEN
MENUITEM "保存(&S)" IDM_SAVE
MENUITEM "另存为" IDM_SAVEAS
MENUITEM SEPARATOR
MENUITEM "退出(&E)" IDM_EXIT
END
POPUP "帮助"
BEGIN
MENUITEM "关于" IDM_ABOUT
END
END
1 24 "res\\xpstyle.manifest"
2. C Progamming & Win32 API
Win32 API因为总是作为业余的娱乐,总不用就忘了。Windows API数量巨大,参数也多,用CFree写提示太少,还是比较别扭的。另外这次有一些新学的东西。
win32
-
移动按钮的位置用MoveWindow()
-
BeginPaint、EndPaint和GetDC、ReleaseDC的区别
-
WM_SIZE和WM_SIZING的区别
-
文件操作。GetOpenFileName()、CreateFile()、GetFileSize()、CloseHandle()函数。
-
内存映射机制,减少内存占用。CreateFileMapping()、MapViewOfFile()、UnmapViewOfFile()
-
强制使窗口重绘
GetClientRect(hwnd, &rc); InvalidateRect(hwnd,&rc,TRUE);
C语言
- 对于void funcA(),其他位置这样引用:return (funcA(), 0),即return 0
- 使用了数组指针,立体声和单声道用同一个Buffer,并且使写出的代码可以更清晰一点。
typedef SHORT(*s16_stereo_t)[2]; typedef SHORT* s16_mono_t; s16_stereo_t buf0 = wav.data; s16_mono_t buf1 = wav.data; int i = 0; (void)buf0[i][0]; (void)buf1[i];
3. 绘制wav波形
绘制时域波形看起来简单但还是有一些学问的。采样点多时,直接抽取是不行的,应该计算各抽取区间的最大最小值再进行绘制。初步试验在44100采样率下,2048点画出来的基本上能反映波形幅值情况,没有必要用所有点的平均,但点数过少时,像128点的时候失真就比较严重了。目前抽取步进小于2048和需要插值的情况暂没做,不过以前做过Hanning Windowed sinc插值的情况,所以不成问题。插值计算量较大,可考虑屏幕显示采样点很少时才插值绘制。实际测试Adobe Audition至少在1600像素显示1000点时就已经平滑绘制了。
获取wav单通道采样数用datasize / blockalign可以得到。
结尾
这个年代使用CFree编Win32程序貌似没什么意义,但是作为娱乐的话,享受这一种搭积木的乐趣,享受其中滋味,还是很快乐的,相信懂的人会懂。今后的练习还将继续围绕DAW相关功能和VST插件展开。
2020-10-26
完成了插值绘制的代码
完成绘制Cursor
绘图逻辑,已完全不闪烁
主要问题点:
- 插值函数的调用。x值取(double)pixelX * (double)len / (double)width四舍五入,取最近的采样点。
InterpPoints(double x, const short* s16p_data, size_t len)
- 一开始绘图速度慢,且拖动窗口时闪烁,主要做了以下优化:
-
由于计算量较大,不在绘制时计算,改到WM_EXITSIZEMOVE中计算,视觉上可以接受;
-
调整窗口大小时,同样由于计算量大,无法实时画出。在调整窗口过程中,只进行当前显示区域的缩放,即:在WM_ENTERSIZEMOVE时保存显示区域的位图,同时g_bPaint置FALSE,让WM_PAINT中绘制显示区域的动作禁止,只使用缩放的位图实时显示。等到WM_EXITSIZEMOVE时,再进行计算并重绘(精细图像)。
/* 缩放 */ StretchBlt(hdc, 0,0,rc.right, rc.bottom - 100, \ g_hdcmem,0,0,g_srcBmpSize.cx, g_srcBmpSize.cy, SRCCOPY);
-
Win32 API不像MFC中可以方便地获得位图的大小。用SetBitmapDimensionEx先保存下来位图的大小,再调用GetBitmapDimensionEx读回。GetBitmapDimensionEx只能返回SetBitmapDimensionEx设置的值(在调试中以为有问题最后没这样写)。
... /* 保存BMP大小 */ SetBitmapDimensionEx(hbmp, Width, Height, NULL); ... ... /* 读取BMP大小 */ SIZE bmpsize; GetBitmapDimensionEx(hbmp, &bmpsize); (void)bmpsize.cx; (void)bmpsize.cy; ...
-
WM_PAINT消息中使用BeginPaint、EndPaint进行绘制。若使用GetDC、ReleaseDC,改变窗口大小时会闪烁,具体原因还没有深究。
2020-10-27
-
完成鼠标滚轮缩放功能。调试时出现问题主要是像素和采样相互转换搞错了:
xCursor = pxlCursor * xViewLen / pxlWidth + xViewOffset; pxlCursor = xCursor - xViewOffset * pxlWidth / xViewLen;
-
待优化抽取绘制。之前因为波形不连续多取了两个点计算最大最小值,实际很难看。只能多取一个点,再额外处理不连续的线。
-
待支持更多格式wav文件:fact、JUNK、bext等字段识别,对24-bit int、32-bit int、float32的支持以及对单声道的支持(比立体声更简单)。
-
待支持选中操作
-
待支持编辑和保存