source: trunk/Utilities/LowLatencyWaveOutPlay/LowLatencyWaveOutPlay.cpp @ 937

Last change on this file since 937 was 164, checked in by roman, 9 years ago

Added signal frequency command line parameter

File size: 5.0 KB
Line 
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
12WAVEFORMATEX g_WaveFormatEx;
13SIZE_T g_nBufferCount;
14ULONG g_nBufferLength;
15ULONG g_nFrequency = 1000;
16ULONG g_nSampleIndex = 0;
17HANDLE g_hDoneEvent;
18volatile LONG g_nDoneCount = 0;
19
20VOID Check(MMRESULT nResult)
21{
22        if(nResult == MMSYSERR_NOERROR)
23                return;
24        _tprintf(_T("nResult %d\n"), nResult);
25        AtlThrow(E_FAIL);
26}
27VOID 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}
36VOID 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}
52int _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> [<signal-frequency>]\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        if(argc > 4)
74                g_nFrequency = _ttoi(argv[4]);
75        HWAVEOUT hWaveOut = NULL;
76        Check(waveOutOpen(&hWaveOut, WAVE_MAPPER, &g_WaveFormatEx, (DWORD_PTR) &waveOutProc, NULL, CALLBACK_FUNCTION));
77        ATLASSERT(hWaveOut);
78        #pragma region Allocate Buffers
79        const SIZE_T nDataSize = (g_WaveFormatEx.nAvgBytesPerSec * g_nBufferLength / 1000) & ~(g_WaveFormatEx.nBlockAlign - 1);
80        _tprintf(_T("Buffer Count: %d\n"), g_nBufferCount);
81        _tprintf(_T("Buffer Length: %d ms (%d bytes)\n"), g_nBufferLength, nDataSize);
82        _tprintf(_T("Signal Frequency: %d Hz\n"), g_nFrequency);
83        CTempBuffer<HGLOBAL> phWaveHeaders(g_nBufferCount);
84        CTempBuffer<WAVEHDR*> ppWaveHeaders(g_nBufferCount);
85        for(SIZE_T nIndex = 0; nIndex < g_nBufferCount; nIndex++)
86        {
87                HGLOBAL& hWaveHeader = phWaveHeaders[nIndex];
88                WAVEHDR*& pWaveHeader = ppWaveHeaders[nIndex];
89                hWaveHeader = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof *pWaveHeader + nDataSize);
90                pWaveHeader = (WAVEHDR*) GlobalLock(hWaveHeader);
91                ATLENSURE_THROW(pWaveHeader, E_OUTOFMEMORY);
92                pWaveHeader->lpData = (LPSTR) (BYTE*) (pWaveHeader + 1);
93                pWaveHeader->dwBufferLength = (DWORD) nDataSize;
94                //pWaveHeader->dwUser =
95                pWaveHeader->dwFlags = 0;
96                pWaveHeader->dwLoops = 0;
97                Check(waveOutPrepareHeader(hWaveOut, pWaveHeader, sizeof *pWaveHeader)); 
98                ATLASSERT(pWaveHeader->dwFlags & WHDR_PREPARED);
99                //GlobalUnlock(hWaveHeader);
100        }
101        #pragma endregion
102        SIZE_T nBufferIndex = 0;
103        g_hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
104        g_nDoneCount = 0;
105        for(SIZE_T nIndex = 0; nIndex < g_nBufferCount; nIndex++)
106        {
107                WAVEHDR* pWaveHeader = ppWaveHeaders[nIndex];
108                Generate(*pWaveHeader);
109                Check(waveOutWrite(hWaveOut, pWaveHeader, sizeof *pWaveHeader)); 
110        }
111        for(; ; )
112        {
113                ATLVERIFY(WaitForSingleObject(g_hDoneEvent, INFINITE) == WAIT_OBJECT_0);
114                for(; ; )
115                {
116                        const LONG nDoneCount = InterlockedDecrement(&g_nDoneCount);
117                        if(nDoneCount < 0)
118                                break;
119                        WAVEHDR* pWaveHeader = ppWaveHeaders[nBufferIndex];
120                        ATLTRACE(_T("nBufferIndex %d, pWaveHeader 0x%p, dwFlags 0x%x\n"), nBufferIndex, pWaveHeader, pWaveHeader->dwFlags);
121                        ATLASSERT(pWaveHeader->dwFlags & WHDR_PREPARED);
122                        ATLASSERT(pWaveHeader->dwFlags & WHDR_DONE);
123                        pWaveHeader->dwFlags &= ~WHDR_DONE;
124                        Generate(*pWaveHeader);
125                        Check(waveOutWrite(hWaveOut, pWaveHeader, sizeof *pWaveHeader)); 
126                        ++nBufferIndex %= g_nBufferCount;
127                }
128                InterlockedIncrement(&g_nDoneCount);
129        }
130        return 0;
131}
132
Note: See TracBrowser for help on using the repository browser.