1 | //////////////////////////////////////////////////////////// |
---|
2 | // Copyright (C) Roman Ryltsov, 2008-2013 |
---|
3 | // Created by Roman Ryltsov roman@alax.info |
---|
4 | |
---|
5 | #include "stdafx.h" |
---|
6 | #define _USE_MATH_DEFINES |
---|
7 | #include <math.h> |
---|
8 | #include <mmsystem.h> |
---|
9 | |
---|
10 | #pragma comment(lib, "winmm.lib") |
---|
11 | |
---|
12 | WAVEFORMATEX g_WaveFormatEx; |
---|
13 | SIZE_T g_nBufferCount; |
---|
14 | ULONG g_nBufferLength; |
---|
15 | ULONG g_nFrequency = 1000; |
---|
16 | ULONG g_nSampleIndex = 0; |
---|
17 | HANDLE g_hDoneEvent; |
---|
18 | volatile LONG g_nDoneCount = 0; |
---|
19 | |
---|
20 | VOID Check(MMRESULT nResult) |
---|
21 | { |
---|
22 | if(nResult == MMSYSERR_NOERROR) |
---|
23 | return; |
---|
24 | _tprintf(_T("nResult %d\n"), nResult); |
---|
25 | AtlThrow(E_FAIL); |
---|
26 | } |
---|
27 | VOID Generate(WAVEHDR& WaveHeader) |
---|
28 | { |
---|
29 | ATLASSERT(g_WaveFormatEx.nChannels == 1 && g_WaveFormatEx.wBitsPerSample == 16 && g_WaveFormatEx.nBlockAlign == 2); |
---|
30 | SHORT* pnData = (SHORT*) WaveHeader.lpData; |
---|
31 | SIZE_T nDataCount = WaveHeader.dwBufferLength / sizeof *pnData; |
---|
32 | for(SIZE_T nIndex = 0; nIndex < nDataCount; nIndex++) |
---|
33 | pnData[nIndex] = (SHORT) (32000 * sin(1.0 * (g_nSampleIndex + nIndex) / g_WaveFormatEx.nSamplesPerSec * g_nFrequency * 2 * M_PI)); |
---|
34 | g_nSampleIndex += (ULONG) nDataCount; |
---|
35 | } |
---|
36 | VOID CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT nMessage, DWORD_PTR nInstance, DWORD_PTR nParameter1, DWORD_PTR nParameter2) |
---|
37 | { |
---|
38 | switch(nMessage) |
---|
39 | { |
---|
40 | case WOM_DONE: |
---|
41 | { |
---|
42 | WAVEHDR* pWaveHeader = (WAVEHDR*) nParameter1; |
---|
43 | ATLASSERT(!nParameter2); |
---|
44 | ATLASSERT(pWaveHeader->dwFlags & WHDR_PREPARED); |
---|
45 | ATLASSERT(pWaveHeader->dwFlags & WHDR_DONE); |
---|
46 | InterlockedIncrement(&g_nDoneCount); |
---|
47 | ATLVERIFY(SetEvent(g_hDoneEvent)); |
---|
48 | } |
---|
49 | break; |
---|
50 | } |
---|
51 | } |
---|
52 | int _tmain(int argc, _TCHAR* argv[]) |
---|
53 | { |
---|
54 | #if defined(_DEBUG) |
---|
55 | AtlTraceLoadSettings(NULL); |
---|
56 | #endif // defined(_DEBUG) |
---|
57 | if(argc <= 3) |
---|
58 | { |
---|
59 | _tprintf(_T("Syntax: LowLatencyWaveOutPlay <sampling-rate> <buffer-count> <buffer-length>\n")); |
---|
60 | _tprintf(_T(" Run 'LowLatencyWaveOutPlay 22050 8 5' to play 1000 Hz signal using 5 buffers, 8 ms long each via default audio output device (at 22050 Hz sampling rate)\n")); |
---|
61 | return 1; |
---|
62 | } |
---|
63 | ZeroMemory(&g_WaveFormatEx, sizeof g_WaveFormatEx); |
---|
64 | g_WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM; |
---|
65 | g_WaveFormatEx.nChannels = 1; |
---|
66 | g_WaveFormatEx.nSamplesPerSec = _ttoi(argv[1]);; |
---|
67 | g_WaveFormatEx.wBitsPerSample = 16; |
---|
68 | g_WaveFormatEx.nBlockAlign = g_WaveFormatEx.nChannels * (g_WaveFormatEx.wBitsPerSample >> 3); |
---|
69 | g_WaveFormatEx.nAvgBytesPerSec = g_WaveFormatEx.nSamplesPerSec * g_WaveFormatEx.nBlockAlign; |
---|
70 | _tprintf(_T("Format: %d Hz, %d channels, %d bits per sample\n"), g_WaveFormatEx.nSamplesPerSec, g_WaveFormatEx.nChannels, g_WaveFormatEx.wBitsPerSample); |
---|
71 | g_nBufferCount = _ttoi(argv[2]); |
---|
72 | g_nBufferLength = _ttoi(argv[3]); |
---|
73 | HWAVEOUT hWaveOut = NULL; |
---|
74 | Check(waveOutOpen(&hWaveOut, WAVE_MAPPER, &g_WaveFormatEx, (DWORD_PTR) &waveOutProc, NULL, CALLBACK_FUNCTION)); |
---|
75 | ATLASSERT(hWaveOut); |
---|
76 | #pragma region Allocate Buffers |
---|
77 | const SIZE_T nDataSize = (g_WaveFormatEx.nAvgBytesPerSec * g_nBufferLength / 1000) & ~(g_WaveFormatEx.nBlockAlign - 1); |
---|
78 | _tprintf(_T("Buffer Count: %d\n"), g_nBufferCount); |
---|
79 | _tprintf(_T("Buffer Length: %d ms (%d bytes)\n"), g_nBufferLength, nDataSize); |
---|
80 | _tprintf(_T("Signal Frequency: %d Hz\n"), g_nFrequency); |
---|
81 | CTempBuffer<HGLOBAL> phWaveHeaders(g_nBufferCount); |
---|
82 | CTempBuffer<WAVEHDR*> ppWaveHeaders(g_nBufferCount); |
---|
83 | for(SIZE_T nIndex = 0; nIndex < g_nBufferCount; nIndex++) |
---|
84 | { |
---|
85 | HGLOBAL& hWaveHeader = phWaveHeaders[nIndex]; |
---|
86 | WAVEHDR*& pWaveHeader = ppWaveHeaders[nIndex]; |
---|
87 | hWaveHeader = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof *pWaveHeader + nDataSize); |
---|
88 | pWaveHeader = (WAVEHDR*) GlobalLock(hWaveHeader); |
---|
89 | ATLENSURE_THROW(pWaveHeader, E_OUTOFMEMORY); |
---|
90 | pWaveHeader->lpData = (LPSTR) (BYTE*) (pWaveHeader + 1); |
---|
91 | pWaveHeader->dwBufferLength = (DWORD) nDataSize; |
---|
92 | //pWaveHeader->dwUser = |
---|
93 | pWaveHeader->dwFlags = 0; |
---|
94 | pWaveHeader->dwLoops = 0; |
---|
95 | Check(waveOutPrepareHeader(hWaveOut, pWaveHeader, sizeof *pWaveHeader)); |
---|
96 | ATLASSERT(pWaveHeader->dwFlags & WHDR_PREPARED); |
---|
97 | //GlobalUnlock(hWaveHeader); |
---|
98 | } |
---|
99 | #pragma endregion |
---|
100 | SIZE_T nBufferIndex = 0; |
---|
101 | g_hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
---|
102 | g_nDoneCount = 0; |
---|
103 | for(SIZE_T nIndex = 0; nIndex < g_nBufferCount; nIndex++) |
---|
104 | { |
---|
105 | WAVEHDR* pWaveHeader = ppWaveHeaders[nIndex]; |
---|
106 | Generate(*pWaveHeader); |
---|
107 | Check(waveOutWrite(hWaveOut, pWaveHeader, sizeof *pWaveHeader)); |
---|
108 | } |
---|
109 | for(; ; ) |
---|
110 | { |
---|
111 | ATLVERIFY(WaitForSingleObject(g_hDoneEvent, INFINITE) == WAIT_OBJECT_0); |
---|
112 | for(; ; ) |
---|
113 | { |
---|
114 | const LONG nDoneCount = InterlockedDecrement(&g_nDoneCount); |
---|
115 | if(nDoneCount < 0) |
---|
116 | break; |
---|
117 | WAVEHDR* pWaveHeader = ppWaveHeaders[nBufferIndex]; |
---|
118 | ATLTRACE(_T("nBufferIndex %d, pWaveHeader 0x%p, dwFlags 0x%x\n"), nBufferIndex, pWaveHeader, pWaveHeader->dwFlags); |
---|
119 | ATLASSERT(pWaveHeader->dwFlags & WHDR_PREPARED); |
---|
120 | ATLASSERT(pWaveHeader->dwFlags & WHDR_DONE); |
---|
121 | pWaveHeader->dwFlags &= ~WHDR_DONE; |
---|
122 | Generate(*pWaveHeader); |
---|
123 | Check(waveOutWrite(hWaveOut, pWaveHeader, sizeof *pWaveHeader)); |
---|
124 | ++nBufferIndex %= g_nBufferCount; |
---|
125 | } |
---|
126 | InterlockedIncrement(&g_nDoneCount); |
---|
127 | } |
---|
128 | return 0; |
---|
129 | } |
---|
130 | |
---|