/* AudioTester v1.7 Copyright © 2015 James Chapman Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "stdafx.h" #include "AudioTester.h" #include #include #include #include "decoders.h" #include "stream.h" #pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #define WINDOWTITLE _TEXT("AudioTester") #define STR_ABOUT _TEXT("AudioTester version 1.7\r\nCopyright © 2015 James Chapman \r\nhttp://www.vuplayer.com\r\n") #define STR_ABOUTMENU _TEXT("About AudioTester") #define STR_START _TEXT("Drop files/folders here") #define STR_FILEERROR _TEXT("UNABLE TO OPEN DECODER") #define STR_STOP _TEXT("Stop") #define STR_RESULT _TEXT("%d %s scanned in %.2f seconds") #define STR_FILE _TEXT("file") #define STR_FILES _TEXT("files") #define STR_ERROR _TEXT("failed") #define STR_PASS _TEXT("passed") #define STR_OK _TEXT("OK") #define STR_VERSION_OGG _TEXT("\r\nlibogg: v1.3.2") #define STR_VERSION_VORBIS _TEXT("\r\nlibvorbis: v1.3.4") #define STR_VERSION_FLAC _TEXT("\r\nlibFLAC: v1.3.1") #define STR_VERSION_WAVPACK _TEXT("\r\nlibWavPack: v4.70") #define PADDING 12 #define BUTTON_WIDTH 80 #define BUTTON_HEIGHT 24 #define MAXCPU 16 #define MAXTEXT 256 #define MSG_TEXT WM_APP+42 #define MSG_FINISHED WM_APP+43 #define MSG_PERCENT WM_APP+44 #define MSG_CMDLINE WM_APP+45 #define ID_ABOUT 1974 #define TIMER 20 #define SHELL_MUSIC 237 void Startup(HWND hwnd); void Cleanup(); void ResizeControls(HWND hwnd); void StartThreads(HWND hwnd); void StopThreads(); void OnDrop(HDROP drop); void CheckFile(const wchar_t* filename); void ScanFolder(const wchar_t* folder); void AddFile(const wchar_t* filename); bool GetNext(wstring& filename); void SetText(wchar_t* filename, wchar_t* error); void SetPending(); void StartTimer(); void StopTimer(); void UpdatePercent(); wchar_t* GetCmdLine(); LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK EditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); DWORD WINAPI ThreadProc(LPVOID param); HINSTANCE g_hInst; HWND g_hWnd; HWND g_hEdit; HWND g_hButton; CDecoders* g_decoders; int g_cpus; int g_filecount; int g_filetotal; DWORD g_timer; float g_elapsed; HANDLE g_hTerminate; HANDLE g_hPending; HANDLE g_hThread[MAXCPU]; HANDLE g_hFinished[MAXCPU]; HWND g_hProgress[MAXCPU]; int g_progress[MAXCPU]; int g_width; int g_cpu; CRITICAL_SECTION g_critical; wstring g_text; wchar_t g_buf[MAXTEXT]; list g_list; list g_passes; map g_errors; WNDPROC g_editproc; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { g_hInst = hInstance; WNDCLASSEX wc; ZeroMemory(&wc, sizeof(WNDCLASSEX)); wc.cbSize = sizeof(WNDCLASSEX); wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hInstance = hInstance; wc.lpfnWndProc = WndProc; wc.lpszClassName = WINDOWTITLE; wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW; wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_AUDIOTESTER)); wc.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SMALL)); if (!RegisterClassEx(&wc)) return 0; HWND h = FindWindow(WINDOWTITLE, NULL); if (h) { // Only allow one instance return 0; } InitCommonControls(); g_hWnd = CreateWindowEx(WS_EX_ACCEPTFILES, WINDOWTITLE, WINDOWTITLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hInstance, 0); if (g_hWnd) { HMENU menu = GetSystemMenu(g_hWnd, 0); InsertMenu(menu, 0, MF_BYPOSITION | MF_SEPARATOR, 0, 0); InsertMenu(menu, 0, MF_BYPOSITION | MF_STRING, ID_ABOUT, STR_ABOUTMENU); ShowWindow(g_hWnd, nCmdShow); wchar_t* cmdline = GetCmdLine(); if (cmdline) { SendMessage(g_hWnd, MSG_CMDLINE, (WPARAM)cmdline, 0); delete[] cmdline; } } else return 0; MSG msg; BOOL bReturn; while (bReturn = GetMessage(&msg, 0, 0, 0)) { if (bReturn == -1) bReturn = 0; else { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int)msg.wParam; } void Startup(HWND hwnd) { g_decoders = new CDecoders; g_hEdit = CreateWindow(_TEXT("EDIT"), 0, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | ES_MULTILINE | ES_READONLY, PADDING, PADDING, 0, 0, hwnd, 0, g_hInst, 0); g_editproc = (WNDPROC)SetWindowLongPtr(g_hEdit, GWLP_WNDPROC, (LONG_PTR)EditProc); g_hButton = CreateWindow(_TEXT("BUTTON"), STR_STOP, WS_CHILD | WS_VISIBLE | WS_DISABLED | BS_CENTER | BS_VCENTER, 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, hwnd, 0, g_hInst, 0); HFONT h = (HFONT)GetStockObject(ANSI_VAR_FONT); SendMessage(g_hButton, WM_SETFONT, (WPARAM)h, 1); SendMessage(g_hEdit, WM_SETFONT, (WPARAM)h, 1); SendMessage(g_hEdit, WM_SETTEXT, 0, (LPARAM)STR_START ); StartThreads(hwnd); SetTimer(hwnd, TIMER, TIMER, 0); } void Cleanup() { KillTimer(g_hWnd, TIMER); StopThreads(); delete g_decoders; } void ResizeControls(HWND hwnd) { RECT rect; GetClientRect(hwnd, &rect); int width = rect.right - rect.left; int height = rect.bottom - rect.top; SetWindowPos(g_hButton, 0, width - PADDING - BUTTON_WIDTH, height - PADDING - BUTTON_HEIGHT, 0, 0, SWP_NOSIZE | SWP_NOZORDER); SetWindowPos(g_hEdit, 0, 0, 0, width - PADDING * 2, height - PADDING * 3 - BUTTON_HEIGHT, SWP_NOMOVE | SWP_NOZORDER); g_width = (width - PADDING*(2 + g_cpus) - BUTTON_WIDTH) / g_cpus; for (int i = 0; iMAXCPU) ? MAXCPU : info.dwNumberOfProcessors; if (g_cpus>1) CDecoder::m_usemem = 1; g_hTerminate = CreateEvent(0, 1, 0, 0); g_hPending = CreateEvent(0, 1, 0, 0); InitializeCriticalSection(&g_critical); for (int i = 0; i1) SetThreadAffinityMask(g_hThread[i], 1 << i); } } void StopThreads() { SetEvent(g_hTerminate); WaitForMultipleObjects(g_cpus, g_hThread, 1, INFINITE); DeleteCriticalSection(&g_critical); } void OnDrop(HDROP drop) { int count = DragQueryFile(drop, 0xffffffff, 0, 0); wchar_t buf[4096]; for (int i = 0; iIsSupportedType(path.c_str())) AddFile(path.c_str()); } SetCursor(old); } void ScanFolder(const wchar_t* folder) { WIN32_FIND_DATA data; wstring path(folder); if (*(folder + wcslen(folder) - 1) != '\\') path += '\\'; wstring str = path + _TEXT("*.*"); HANDLE h = FindFirstFile(str.c_str(), &data); if (h != INVALID_HANDLE_VALUE) { BOOL b = 1; while (b) { str = path + data.cFileName; if (GetFileAttributes(str.c_str())&FILE_ATTRIBUTE_DIRECTORY) { if (data.cFileName[0] != '.') ScanFolder(str.c_str()); } else { if (g_decoders->IsSupportedType(str.c_str())) AddFile(str.c_str()); } b = FindNextFile(h, &data); } FindClose(h); } } void AddFile(const wchar_t* filename) { EnterCriticalSection(&g_critical); g_list.push_back(filename); LeaveCriticalSection(&g_critical); g_filetotal++; } bool GetNext(wstring& filename) { bool success = 1; EnterCriticalSection(&g_critical); if (g_list.empty()) success = 0; else { filename = g_list.front(); g_list.pop_front(); } LeaveCriticalSection(&g_critical); if (!success) ResetEvent(g_hPending); return success; } void SetText(wchar_t* filename, wchar_t* error) { if (filename) { swprintf_s(g_buf, MAXTEXT, _TEXT("[%d/%d] "), ++g_filecount, g_filetotal); g_text = g_buf; g_text += filename + 4; if (error) g_errors[filename + 4] = error; else g_passes.push_back(filename + 4); } else { swprintf(g_buf, MAXTEXT, STR_RESULT, g_filecount, (g_filecount == 1) ? (STR_FILE) : (STR_FILES), g_filecount ? g_elapsed : 0); g_text = g_buf; swprintf(g_buf, MAXTEXT, _TEXT("\r\n---\r\n%d %s %s"), g_errors.size(), (g_errors.size() == 1) ? (STR_FILE) : (STR_FILES), STR_ERROR); g_text += g_buf; for (map::const_iterator i = g_errors.begin(); i != g_errors.end(); i++) { g_text += _TEXT("\r\n"); g_text += (*i).first; if (!(*i).second.empty()) { g_text += _TEXT("\t("); g_text += (*i).second; g_text += ')'; } } swprintf(g_buf, MAXTEXT, _TEXT("\r\n---\r\n%d %s %s"), g_passes.size(), (g_passes.size() == 1) ? (STR_FILE) : (STR_FILES), STR_PASS); g_text += g_buf; g_passes.sort(); for (list::const_iterator i = g_passes.begin(); i != g_passes.end(); i++) { g_text += _TEXT("\r\n"); g_text += *i; } } g_text += _TEXT("\r\n"); SetWindowText(g_hEdit, (g_text.c_str())); if (filename) delete[] filename; if (error) delete[] error; } void SetPending() { if (WaitForMultipleObjects(g_cpus, g_hFinished, 1, 0) == WAIT_OBJECT_0) StartTimer(); SetEvent(g_hPending); EnableWindow(g_hButton, 1); UpdatePercent(); } void StartTimer() { g_timer = GetTickCount(); } void StopTimer() { DWORD i = GetTickCount(); g_elapsed = (i1) { const size_t bufSize = wcslen(arglist[1]) + 1; buf = new wchar_t[bufSize]; wcscpy_s(buf, bufSize, arglist[1]); } LocalFree(arglist); } return buf; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_CREATE: Startup(hwnd); return 0; case WM_DESTROY: Cleanup(); PostQuitMessage(0); return 0; case WM_CLOSE: DestroyWindow(hwnd); return 0; case WM_SIZE: if (wParam != SIZE_MINIMIZED) ResizeControls(hwnd); return 0; case WM_DROPFILES: OnDrop((HDROP)wParam); return 0; case WM_COMMAND: if (HIWORD(wParam) == BN_CLICKED) { EnterCriticalSection(&g_critical); g_list.clear(); LeaveCriticalSection(&g_critical); EnableWindow(g_hButton, 0); return 0; } break; case WM_SYSCOMMAND: if (wParam == ID_ABOUT) { wstring message(STR_ABOUT); message+=STR_VERSION_OGG; message+=STR_VERSION_VORBIS; message+=STR_VERSION_FLAC; message+=STR_VERSION_WAVPACK; MessageBox(hwnd, message.c_str(), WINDOWTITLE, MB_ICONINFORMATION | MB_OK); return 0; } break; case WM_COPYDATA: if (((PCOPYDATASTRUCT)lParam)->dwData == ID_ABOUT) { CheckFile((wchar_t*)(((PCOPYDATASTRUCT)lParam)->lpData)); SetPending(); } return 1; case WM_TIMER: for (g_cpu = 0; g_cpuOpenDecoder(filename.c_str()); LeaveCriticalSection(&g_critical); if (decoder) { samplecount = decoder->GetSampleCount(); if (!samplecount) samplecount = 0xffffff; PostMessage(g_hProgress[n], PBM_SETRANGE32, 0, (int)(samplecount >> 8)); total = 0; count = decoder->Read(); while ((WaitForSingleObject(g_hTerminate, 0) != WAIT_OBJECT_0) && (count>0)) { total += count; g_progress[n] = (int)(total >> 8); count = decoder->Read(); } if (count<0) { const size_t bufSize = wcslen(decoder->GetLastError()) + 1; error = new wchar_t[bufSize]; wcscpy_s(error, bufSize, decoder->GetLastError()); } else error = 0; delete decoder; } else { const size_t bufSize = wcslen(STR_FILEERROR) + 1; error = new wchar_t[bufSize]; wcscpy_s(error, bufSize, STR_FILEERROR); } PostMessage(g_hWnd, MSG_TEXT, (WPARAM)file, (WPARAM)error); PostMessage(g_hWnd, MSG_PERCENT, 0, 0); } g_progress[n] = 0; PostMessage(g_hWnd, MSG_FINISHED, n, 0); a = WaitForMultipleObjects(2, h, 0, INFINITE); } return 0; }