// WaveGraph5 Waveファイルグラフ表示
#pragma comment(lib, "winmm")
#include <Windows.h>
#include <tchar.h>
#define SAFE_FREE(p) if (p) { free(p); p = NULL; }
#define APP_NAME TEXT("WaveGraph")
// 関数プロトタイプ宣言
void Trace(LPCTSTR format, ...);
BOOL Load(LPTSTR pszFileName);
BOOL ReadWaveFile(LPTSTR pszFileName);
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void ScrollInfo(HWND hWnd);
void OnDropFiles(HWND hWnd, WPARAM wParam);
void OnSize(HWND hWnd, WPARAM wParam, LPARAM lParam);
void OnHScroll(HWND hWnd, WPARAM wParam);
void OnPaint(HWND hWnd);
void PaintWaveform(HDC hdc, LONG top, LONG bottom, RECT rcPaint, int ch);
void SetTitle(HWND hWnd);
// 外部変数
SCROLLINFO siHorz;
PBYTE waveformData = NULL;
int waveformLen = 0;
WAVEFORMATEX wfx;
TCHAR szFileName[MAX_PATH];
//==============================================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
{
// プログラム引数
LPTSTR lpCmdLine = GetCommandLine();
int argc;
LPTSTR *argv = CommandLineToArgvW(lpCmdLine, &argc);
if (2 <= argc) {
Load(argv[1]);
}
// ウィンドウクラスの登録
WNDCLASSEX wcx;
ZeroMemory(&wcx, sizeof wcx);
wcx.cbSize = sizeof wcx;
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = WndProc;
wcx.hInstance = hInstance;
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcx.lpszClassName = APP_NAME;
if (RegisterClassEx(&wcx) == 0) {
return 0;
}
// ウィンドウの作成
HWND hWnd = CreateWindowEx(
WS_EX_ACCEPTFILES,
APP_NAME, APP_NAME,
WS_OVERLAPPEDWINDOW | WS_HSCROLL,
CW_USEDEFAULT, 0,
CW_USEDEFAULT, 0,
NULL, NULL, hInstance, NULL);
if (hWnd == NULL) {
return 0;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
SetTitle(hWnd);
// メッセージループ
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
void Trace(LPCTSTR format, ...)
{
va_list arg_ptr;
TCHAR buffer[256];
int size;
va_start(arg_ptr, format);
size = _vsntprintf_s(buffer, _countof(buffer), _TRUNCATE, format, arg_ptr);
va_end(arg_ptr);
OutputDebugString(buffer);
if (size < 0) {
OutputDebugString(_T("...\n"));
}
}
BOOL Load(LPTSTR pszFileName)
{
BOOL br = ReadWaveFile(pszFileName);
_tcscpy_s(szFileName, pszFileName);
return br;
}
BOOL ReadWaveFile(LPTSTR pszFileName)
{
MMRESULT mmr;
SAFE_FREE(waveformData);
waveformLen = 0;
// Open
HMMIO hmmio = mmioOpen(pszFileName, NULL, MMIO_READ);
if (hmmio == NULL) {
return FALSE;
}
// RIFFチャンク
MMCKINFO ckParent;
ckParent.fccType = mmioFOURCC('W','A','V','E');
mmr = mmioDescend(hmmio, &ckParent, NULL, MMIO_FINDRIFF);
if (mmr != MMSYSERR_NOERROR) {
return FALSE;
}
// fmtチャンク
MMCKINFO ckSub;
ckSub.ckid = mmioFOURCC('f','m','t',' ');
mmr = mmioDescend(hmmio, &ckSub, &ckParent, MMIO_FINDCHUNK);
if (mmr != MMSYSERR_NOERROR) {
return FALSE;
}
LONG read = mmioRead(hmmio, (HPSTR)&wfx, 16);
if (read != 16) {
return FALSE;
}
mmioAscend(hmmio, &ckSub, 0);
// dataチャンク
ckSub.ckid = mmioFOURCC('d','a','t','a');
mmr = mmioDescend(hmmio, &ckSub, &ckParent, MMIO_FINDCHUNK);
if (mmr != MMSYSERR_NOERROR) {
return FALSE;
}
waveformData = (PBYTE)malloc(ckSub.cksize);
if (waveformData == NULL) {
return FALSE;
}
read = mmioRead(hmmio, (HPSTR)waveformData, ckSub.cksize);
if (read != ckSub.cksize) {
return FALSE;
}
waveformLen = ckSub.cksize / wfx.nBlockAlign;
mmioAscend(hmmio, &ckSub, 0);
// RIFFチャンク
mmioAscend(hmmio, &ckParent, 0);
// Close
mmioClose(hmmio, 0);
return TRUE;
}
//------------------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_PAINT:
OnPaint(hWnd);
return 0;
case WM_HSCROLL:
OnHScroll(hWnd, wParam);
return 0;
case WM_SIZE:
OnSize(hWnd, wParam, lParam);
return 0;
case WM_DROPFILES:
OnDropFiles(hWnd, wParam);
DragFinish((HDROP)wParam);
return 0;
case WM_CREATE:
ScrollInfo(hWnd);
return 0;
case WM_DESTROY:
SAFE_FREE(waveformData);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
void ScrollInfo(HWND hWnd)
{
RECT rc;
GetClientRect(hWnd, &rc);
siHorz.cbSize = sizeof siHorz;
siHorz.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
siHorz.nMin = 0;
siHorz.nMax = waveformLen - 1;
siHorz.nPage = rc.right;
siHorz.nPos = 0;
SetScrollInfo(hWnd, SB_HORZ, &siHorz, FALSE);
}
void OnDropFiles(HWND hWnd, WPARAM wParam)
{
HDROP hDrop = (HDROP)wParam;
TCHAR szFile[MAX_PATH];
DragQueryFile(hDrop, 0, szFile, _countof(szFile));
Load(szFile);
SetTitle(hWnd);
ScrollInfo(hWnd);
InvalidateRect(hWnd, NULL, TRUE);
}
void OnSize(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
if (wParam == SIZE_MINIMIZED) return;
siHorz.nPage = LOWORD(lParam);
int nPosMax = max(siHorz.nMax - (int)siHorz.nPage + 1, 0);
siHorz.nPos = min(siHorz.nPos, nPosMax);
SetScrollInfo(hWnd, SB_HORZ, &siHorz, TRUE);
}
void OnHScroll(HWND hWnd, WPARAM wParam)
{
int nPos = siHorz.nPos;
switch (LOWORD(wParam)) {
case SB_LINEUP:
nPos -= 10;
break;
case SB_LINEDOWN:
nPos += 10;
break;
case SB_PAGEUP:
nPos -= siHorz.nPage;
break;
case SB_PAGEDOWN:
nPos += siHorz.nPage;
break;
case SB_THUMBTRACK:
SCROLLINFO si;
si.cbSize = sizeof si;
si.fMask = SIF_TRACKPOS;
if (GetScrollInfo(hWnd, SB_HORZ, &si) != 0) {
nPos = si.nTrackPos;
}
break;
}
int nPosMax = max(siHorz.nMax - (int)siHorz.nPage + 1, 0);
nPos = min(nPos, nPosMax);
nPos = max(nPos, 0);
if (nPos == siHorz.nPos) return;
ScrollWindowEx(hWnd, siHorz.nPos - nPos, 0,
NULL, NULL, NULL, NULL, SW_INVALIDATE | SW_ERASE);
siHorz.nPos = nPos;
SetScrollInfo(hWnd, SB_HORZ, &siHorz, TRUE);
UpdateWindow(hWnd);
}
void OnPaint(HWND hWnd)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rc;
GetClientRect(hWnd, &rc);
if (waveformData) {
switch (wfx.nChannels) {
case 1:
PaintWaveform(hdc, rc.top, rc.bottom, ps.rcPaint, 0);
break;
case 2:
LONG center = rc.bottom / 2;
PaintWaveform(hdc, rc.top, center, ps.rcPaint, 0);
PaintWaveform(hdc, center + 1, rc.bottom, ps.rcPaint, 1);
MoveToEx(hdc, 0, center, NULL);
LineTo(hdc, rc.right, center);
break;
}
}
EndPaint(hWnd, &ps);
}
void PaintWaveform(HDC hdc, LONG top, LONG bottom, RECT rcPaint, int ch)
{
LONG height = bottom - top;
HPEN pen = CreatePen(PS_SOLID, 0, RGB(0,0,255));
HGDIOBJ penOld = SelectObject(hdc, pen);
for (int x = rcPaint.left; x < rcPaint.right; x++) {
int i = siHorz.nPos + x;
if (waveformLen <= i) break;
int y;
if (wfx.wBitsPerSample == 8) {
y = top + height * (255 - waveformData[wfx.nChannels * i + ch]) / 256;
} else {
y = top + height * (32767 - ((short*)waveformData)[wfx.nChannels * i + ch]) / 65536;
}
MoveToEx(hdc, x, top + height / 2, NULL);
LineTo(hdc, x, y);
}
SelectObject(hdc, penOld);
DeleteObject(pen);
}
void SetTitle(HWND hWnd)
{
if (waveformData == NULL) {
SetWindowText(hWnd, APP_NAME);
return;
}
TCHAR str[512];
_stprintf_s(str, _T("%s [%uHz %ubit %uch] - %s"),
szFileName, wfx.nSamplesPerSec, wfx.wBitsPerSample, wfx.nChannels, APP_NAME);
SetWindowText(hWnd, str);
}