使用Win32 API绘制wav文件波形

最近想把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)
    
  • 一开始绘图速度慢,且拖动窗口时闪烁,主要做了以下优化:
  1. 由于计算量较大,不在绘制时计算,改到WM_EXITSIZEMOVE中计算,视觉上可以接受;

  2. 调整窗口大小时,同样由于计算量大,无法实时画出。在调整窗口过程中,只进行当前显示区域的缩放,即:在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);
    
  3. 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;
    	...
    
    
  4. 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的支持以及对单声道的支持(比立体声更简单)。

  • 待支持选中操作

  • 待支持编辑和保存

你可能感兴趣的:(C/C++笔记,音频,编程笔记,c语言,winapi,gdi/gdi+)