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

Last change on this file since 162 was 162, checked in by roman, 10 years ago
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>\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
Note: See TracBrowser for help on using the repository browser.