/*------------------------------------------------------
SINEWAVE.C -- Multimedia Windows Sine Wave Generator
(c) Charles Petzold, 1998
------------------------------------------------------*/
#include <windows.h>
#include <math.h>
#include "resource.h"
#define SAMPLE_RATE 11025
#define FREQ_MIN 20
#define FREQ_MAX 5000
#define FREQ_INIT 440
#define OUT_BUFFER_SIZE 4096
#define PI 3.14159
BOOL CALLBACK DlgProc (HWND, UINT, WPARAM, LPARAM) ;
TCHAR szAppName [] = TEXT ("SineWave") ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
if (-1 == DialogBox (hInstance, szAppName, NULL, DlgProc))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
}
return 0 ;
}
VOID FillBuffer (PBYTE pBuffer, int iFreq)
{
static double fAngle ;
int i ;
for (i = 0 ; i < OUT_BUFFER_SIZE ; i++)
{
pBuffer [i] = (BYTE) (127 + 127 * sin (fAngle)) ;
fAngle += 2 * PI * iFreq / SAMPLE_RATE ;
if (fAngle > 2 * PI)
fAngle -= 2 * PI ;
}
}
BOOL CALLBACK DlgProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static BOOL bShutOff, bClosing ;
static HWAVEOUT hWaveOut ;
static HWND hwndScroll ;
static int iFreq = FREQ_INIT ;
static PBYTE pBuffer1, pBuffer2 ;
static PWAVEHDR pWaveHdr1, pWaveHdr2 ;
static WAVEFORMATEX waveformat ;
int iDummy ;
switch (message)
{
case WM_INITDIALOG:
// 获取滚动条句柄
hwndScroll = GetDlgItem (hwnd, IDC_SCROLL) ;
// 设置滚动条范围
SetScrollRange (hwndScroll, SB_CTL, FREQ_MIN, FREQ_MAX, FALSE) ;
// 设置滚动条方块的位置
SetScrollPos (hwndScroll, SB_CTL, FREQ_INIT, TRUE) ;
// 向IDC_TEXT文字控件写入数值FREQ_INIT
SetDlgItemInt (hwnd, IDC_TEXT, FREQ_INIT, FALSE) ;
return TRUE ;
case WM_HSCROLL:
// 记录滚动条滚动位置
switch (LOWORD (wParam))
{
case SB_LINELEFT: iFreq -= 1 ; break ;
case SB_LINERIGHT: iFreq += 1 ; break ;
case SB_PAGELEFT: iFreq /= 2 ; break ;
case SB_PAGERIGHT: iFreq *= 2 ; break ;
case SB_THUMBTRACK:
iFreq = HIWORD (wParam) ;
break ;
case SB_TOP:
GetScrollRange (hwndScroll, SB_CTL, &iFreq, &iDummy) ;
break ;
case SB_BOTTOM:
GetScrollRange (hwndScroll, SB_CTL, &iDummy, &iFreq) ;
break ;
}
iFreq = max (FREQ_MIN, min (FREQ_MAX, iFreq)) ;
// 设置滚动条滚动后的位置,更改IDC_TEXT文字控件显示数值
SetScrollPos (hwndScroll, SB_CTL, iFreq, TRUE) ;
SetDlgItemInt (hwnd, IDC_TEXT, iFreq, FALSE) ;
return TRUE ;
case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDC_ONOFF:
// 如果音频设备未打开则执行
if (hWaveOut == NULL)
{
// 为音频缓冲区标头结构和音频缓冲区分配内存
pWaveHdr1 = malloc (sizeof (WAVEHDR)) ;
pWaveHdr2 = malloc (sizeof (WAVEHDR)) ;
pBuffer1 = malloc (OUT_BUFFER_SIZE) ;
pBuffer2 = malloc (OUT_BUFFER_SIZE) ;
// 内存分配检查
if (!pWaveHdr1 || !pWaveHdr2 || !pBuffer1 || !pBuffer2)
{
if (!pWaveHdr1) free (pWaveHdr1) ;
if (!pWaveHdr2) free (pWaveHdr2) ;
if (!pBuffer1) free (pBuffer1) ;
if (!pBuffer2) free (pBuffer2) ;
MessageBeep (MB_ICONEXCLAMATION) ;
MessageBox (hwnd, TEXT ("Error allocating memory!"),
szAppName, MB_ICONEXCLAMATION | MB_OK) ;
return TRUE ;
}
// 播放开关标记
bShutOff = FALSE ;
// 定义波形音频数据格式
waveformat.wFormatTag = WAVE_FORMAT_PCM ;
waveformat.nChannels = 1 ;
waveformat.nSamplesPerSec = SAMPLE_RATE ;
waveformat.nAvgBytesPerSec = SAMPLE_RATE ;
waveformat.nBlockAlign = 1 ;
waveformat.wBitsPerSample = 8 ;
waveformat.cbSize = 0 ;
// 打开播放波形音频的输出设备,会发送MM_WOM_OPEN消息给程序
if (waveOutOpen (&hWaveOut, WAVE_MAPPER, &waveformat,
(DWORD) hwnd, 0, CALLBACK_WINDOW)
!= MMSYSERR_NOERROR)
{
free (pWaveHdr1) ;
free (pWaveHdr2) ;
free (pBuffer1) ;
free (pBuffer2) ;
hWaveOut = NULL ;
MessageBeep (MB_ICONEXCLAMATION) ;
MessageBox (hwnd,
TEXT ("Error opening waveform audio device!"),
szAppName, MB_ICONEXCLAMATION | MB_OK) ;
return TRUE ;
}
// 准备一个用于播放波形音频数据块
pWaveHdr1->lpData = pBuffer1 ;
pWaveHdr1->dwBufferLength = OUT_BUFFER_SIZE ;
pWaveHdr1->dwBytesRecorded = 0 ;
pWaveHdr1->dwUser = 0 ;
pWaveHdr1->dwFlags = 0 ;
pWaveHdr1->dwLoops = 1 ;
pWaveHdr1->lpNext = NULL ;
pWaveHdr1->reserved = 0 ;
waveOutPrepareHeader (hWaveOut, pWaveHdr1,
sizeof (WAVEHDR)) ;
pWaveHdr2->lpData = pBuffer2 ;
pWaveHdr2->dwBufferLength = OUT_BUFFER_SIZE ;
pWaveHdr2->dwBytesRecorded = 0 ;
pWaveHdr2->dwUser = 0 ;
pWaveHdr2->dwFlags = 0 ;
pWaveHdr2->dwLoops = 1 ;
pWaveHdr2->lpNext = NULL ;
pWaveHdr2->reserved = 0 ;
waveOutPrepareHeader (hWaveOut, pWaveHdr2,
sizeof (WAVEHDR)) ;
}
else
{
bShutOff = TRUE ;
// 停止在给定的波形音频输出设备上的播放,并重置调用句柄为NULL
waveOutReset (hWaveOut) ;
}
return TRUE ;
}
break ;
// 消息来自waveOutOpen调用
case MM_WOM_OPEN:
SetDlgItemText (hwnd, IDC_ONOFF, TEXT ("Turn Off")) ;
// 配置波形数据
FillBuffer (pBuffer1, iFreq) ;
// 向给定的波形音频输出设备发送一个数据块用于播放
waveOutWrite (hWaveOut, pWaveHdr1, sizeof (WAVEHDR)) ;
FillBuffer (pBuffer2, iFreq) ;
waveOutWrite (hWaveOut, pWaveHdr2, sizeof (WAVEHDR)) ;
return TRUE ;
// 当波形硬件播放完waveOutWrite函数传送来的数据后或来自waveOutReset调用
case MM_WOM_DONE:
// 判断播放开关
if (bShutOff)
{
// 关闭指定的波形音频输出设备
waveOutClose (hWaveOut) ;
return TRUE ;
}
// 填写并发送了一个新的缓冲区
FillBuffer (((PWAVEHDR) lParam)->lpData, iFreq) ;
waveOutWrite (hWaveOut, (PWAVEHDR) lParam, sizeof (WAVEHDR)) ;
return TRUE ;
// 消息由waveOutClose调用后发出
case MM_WOM_CLOSE:
// 释放waveOutPrepareHeader准备的内存块
waveOutUnprepareHeader (hWaveOut, pWaveHdr1, sizeof (WAVEHDR)) ;
waveOutUnprepareHeader (hWaveOut, pWaveHdr2, sizeof (WAVEHDR)) ;
free (pWaveHdr1) ;
free (pWaveHdr2) ;
free (pBuffer1) ;
free (pBuffer2) ;
hWaveOut = NULL ;
SetDlgItemText (hwnd, IDC_ONOFF, TEXT ("Turn On")) ;
if (bClosing)
EndDialog (hwnd, 0) ;
return TRUE ;
case WM_SYSCOMMAND:
switch (wParam)
{
case SC_CLOSE:
if (hWaveOut != NULL)
{
bShutOff = TRUE ;
bClosing = TRUE ;
// 必须先停止播放,才能关闭设备
waveOutReset (hWaveOut) ;
}
else
EndDialog (hwnd, 0) ;
return TRUE ;
}
break ;
}
return FALSE ;
}