/*----------------------------------------
RECORD1.C -- Waveform Audio Recorder
(c) Charles Petzold, 1998
----------------------------------------*/
#include <windows.h>
#include "resource.h"
#define INP_BUFFER_SIZE 16384
BOOL CALLBACK DlgProc (HWND, UINT, WPARAM, LPARAM) ;
TCHAR szAppName [] = TEXT ("Record1") ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
if (-1 == DialogBox (hInstance, TEXT ("Record"), NULL, DlgProc))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
}
return 0 ;
}
void ReverseMemory (BYTE * pBuffer, int iLength)
{
BYTE b ;
int i ;
for (i = 0 ; i < iLength / 2 ; i++)
{
b = pBuffer [i] ;
pBuffer [i] = pBuffer [iLength - i - 1] ;
pBuffer [iLength - i - 1] = b ;
}
}
BOOL CALLBACK DlgProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static BOOL bRecording, bPlaying, bReverse, bPaused,
bEnding, bTerminating ;
static DWORD dwDataLength, dwRepetitions = 1 ;
static HWAVEIN hWaveIn ;
static HWAVEOUT hWaveOut ;
static PBYTE pBuffer1, pBuffer2, pSaveBuffer, pNewBuffer ;
static PWAVEHDR pWaveHdr1, pWaveHdr2 ;
static TCHAR szOpenError[] = TEXT ("Error opening waveform audio!");
static TCHAR szMemError [] = TEXT ("Error allocating memory!") ;
static WAVEFORMATEX waveform ;
switch (message)
{
case WM_INITDIALOG:
// 波形文件数据头内存分配
pWaveHdr1 = malloc (sizeof (WAVEHDR)) ;
pWaveHdr2 = malloc (sizeof (WAVEHDR)) ;
// 保存缓冲区分配内存
pSaveBuffer = malloc (1) ;
return TRUE ;
case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDC_RECORD_BEG: // 按下录音按钮
// 分配缓冲存储器
pBuffer1 = malloc (INP_BUFFER_SIZE) ;
pBuffer2 = malloc (INP_BUFFER_SIZE) ;
if (!pBuffer1 || !pBuffer2)
{
if (pBuffer1) free (pBuffer1) ;
if (pBuffer2) free (pBuffer2) ;
MessageBeep (MB_ICONEXCLAMATION) ;
MessageBox (hwnd, szMemError, szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
return TRUE ;
}
// 定义波形音频数据格式
// 波形音频格式类型 WAVE_FORMAT_PCM 一个或两个通道的PCM数据
waveform.wFormatTag = WAVE_FORMAT_PCM ;
// 波形音频数据通道,单声道的数据使用一个通道
waveform.nChannels = 1 ;
// 采样率
waveform.nSamplesPerSec = 11025 ;
// 所需的平均数据传输速率
waveform.nAvgBytesPerSec = 11025 ;
/* 对于PCM取样方式,nBlockAlign字段设定为nChannels乘以wBitsPerSample再除以8所得到的数值,
它表示每次取样的总字节数*/
waveform.nBlockAlign = 1 ;
// 每个样本占位大小 = 8 或 16
waveform.wBitsPerSample = 8 ;
// 用于非PCM格式的额外信息
waveform.cbSize = 0 ;
// 打开特定的波形记录音频输入设备
if (waveInOpen (&hWaveIn, WAVE_MAPPER, &waveform,
(DWORD) hwnd, 0, CALLBACK_WINDOW))
{
free (pBuffer1) ;
free (pBuffer2) ;
MessageBeep (MB_ICONEXCLAMATION) ;
MessageBox (hwnd, szOpenError, szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
}
// 初始化一个准备数据标头
pWaveHdr1->lpData = pBuffer1 ;
pWaveHdr1->dwBufferLength = INP_BUFFER_SIZE ;
pWaveHdr1->dwBytesRecorded = 0 ;
pWaveHdr1->dwUser = 0 ;
pWaveHdr1->dwFlags = 0 ;
pWaveHdr1->dwLoops = 1 ;
pWaveHdr1->lpNext = NULL ;
pWaveHdr1->reserved = 0 ;
// 让系统准备一个波形音频输入缓冲区
waveInPrepareHeader (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;
pWaveHdr2->lpData = pBuffer2 ;
pWaveHdr2->dwBufferLength = INP_BUFFER_SIZE ;
pWaveHdr2->dwBytesRecorded = 0 ;
pWaveHdr2->dwUser = 0 ;
pWaveHdr2->dwFlags = 0 ;
pWaveHdr2->dwLoops = 1 ;
pWaveHdr2->lpNext = NULL ;
pWaveHdr2->reserved = 0 ;
waveInPrepareHeader (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;
return TRUE ;
case IDC_RECORD_END:
bEnding = TRUE ;
/*停止在给定的波形音频输入设备的输入和复位当前句柄为NULL,
所有等待的缓冲区标记为已完成并返回给应用程序。*/
waveInReset (hWaveIn) ;
return TRUE ;
case IDC_PLAY_BEG:
waveform.wFormatTag = WAVE_FORMAT_PCM ;
waveform.nChannels = 1 ;
waveform.nSamplesPerSec = 11025 ;
waveform.nAvgBytesPerSec = 11025 ;
waveform.nBlockAlign = 1 ;
waveform.wBitsPerSample = 8 ;
waveform.cbSize = 0 ;
// 打开一个波形输出设备,发送MM_WOM_OPEN消息
if (waveOutOpen (&hWaveOut, WAVE_MAPPER, &waveform,
(DWORD) hwnd, 0, CALLBACK_WINDOW))
{
MessageBeep (MB_ICONEXCLAMATION) ;
MessageBox (hwnd, szOpenError, szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
}
return TRUE ;
case IDC_PLAY_PAUSE:
// 暂停或重新开始输出
if (!bPaused)
{
// 暂停波形音频输出设备
waveOutPause (hWaveOut) ;
// 更改按钮文字
SetDlgItemText (hwnd, IDC_PLAY_PAUSE, TEXT ("Resume")) ;
bPaused = TRUE ;
}
else
{
// 恢复暂停播放的波形音频输出设备
waveOutRestart (hWaveOut) ;
SetDlgItemText (hwnd, IDC_PLAY_PAUSE, TEXT ("Pause")) ;
bPaused = FALSE ;
}
return TRUE ;
case IDC_PLAY_END:
// Reset output for close preparation
bEnding = TRUE ;
// 停止波形音频输出设备上的播放并重置当前句柄为NULL,发送MM_WOM_DONE消息
waveOutReset (hWaveOut) ;
return TRUE ;
case IDC_PLAY_REV:
bReverse = TRUE ;
// 将缓冲区数据反向
ReverseMemory (pSaveBuffer, dwDataLength) ;
// 发送播放按钮点击消息
SendMessage (hwnd, WM_COMMAND, IDC_PLAY_BEG, 0) ;
return TRUE ;
case IDC_PLAY_REP:
// 设置循环次数为无限,发送播放按钮点击消息
dwRepetitions = -1 ;
SendMessage (hwnd, WM_COMMAND, IDC_PLAY_BEG, 0) ;
return TRUE ;
case IDC_PLAY_SPEED:
// 将采样率提高一倍,加快音频播放速度
waveform.wFormatTag = WAVE_FORMAT_PCM ;
waveform.nChannels = 1 ;
waveform.nSamplesPerSec = 22050 ;
waveform.nAvgBytesPerSec = 22050 ;
waveform.nBlockAlign = 1 ;
waveform.wBitsPerSample = 8 ;
waveform.cbSize = 0 ;
if (waveOutOpen (&hWaveOut, 0, &waveform, (DWORD) hwnd, 0,
CALLBACK_WINDOW))
{
MessageBeep (MB_ICONEXCLAMATION) ;
MessageBox (hwnd, szOpenError, szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
}
return TRUE ;
}
break ;
// 消息由waveInOpen调用发出
case MM_WIM_OPEN:
// 改变内存大小到1字节,自动释放先前内存空间
pSaveBuffer = realloc (pSaveBuffer, 1) ;
// 启用和禁用按钮
EnableWindow (GetDlgItem (hwnd, IDC_RECORD_BEG), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_RECORD_END), TRUE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_BEG), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_PAUSE), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_END), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_REV), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_REP), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_SPEED), FALSE) ;
SetFocus (GetDlgItem (hwnd, IDC_RECORD_END)) ;
// 向波形音频输入设备发送一个给定的输入缓冲区,用于记录输入的波形
waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;
waveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;
bRecording = TRUE ;
bEnding = FALSE ;
dwDataLength = 0 ;
// 启动波形输入设备,开始采样
waveInStart (hWaveIn) ;
return TRUE ;
// 消息由用于采样的缓冲区填满或调用waveInReset发出
case MM_WIM_DATA:
// 增加pSaveBuffer内存大小
pNewBuffer = realloc (pSaveBuffer, dwDataLength +
// 返回的WAVEHD结构中的dwBytesRecorded字段记录了录入的数据大小
((PWAVEHDR) lParam)->dwBytesRecorded) ;
if (pNewBuffer == NULL)
{
waveInClose (hWaveIn) ;
MessageBeep (MB_ICONEXCLAMATION) ;
MessageBox (hwnd, szMemError, szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
return TRUE ;
}
pSaveBuffer = pNewBuffer ;
// 复制录入的数据到新增加的内存中
CopyMemory (pSaveBuffer + dwDataLength, ((PWAVEHDR) lParam)->lpData,
((PWAVEHDR) lParam)->dwBytesRecorded) ;
// 累加录入数据长度
dwDataLength += ((PWAVEHDR) lParam)->dwBytesRecorded ;
// 判断消息是否由调用waveInReset发出
if (bEnding)
{
waveInClose (hWaveIn) ;
return TRUE ;
}
// 继续录入
waveInAddBuffer (hWaveIn, (PWAVEHDR) lParam, sizeof (WAVEHDR)) ;
return TRUE ;
// 消息由waveInClose调用发出
case MM_WIM_CLOSE:
// 释放数据
waveInUnprepareHeader (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;
waveInUnprepareHeader (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;
free (pBuffer1) ;
free (pBuffer2) ;
// 启用和禁用按钮,设置焦点
EnableWindow (GetDlgItem (hwnd, IDC_RECORD_BEG), TRUE) ;
EnableWindow (GetDlgItem (hwnd, IDC_RECORD_END), FALSE) ;
SetFocus (GetDlgItem (hwnd, IDC_RECORD_BEG)) ;
// 做数据有效性检查,并设置播放按钮
if (dwDataLength > 0)
{
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_BEG), TRUE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_PAUSE), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_END), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_REP), TRUE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_REV), TRUE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_SPEED), TRUE) ;
SetFocus (GetDlgItem (hwnd, IDC_PLAY_BEG)) ;
}
// 设置录音标记
bRecording = FALSE ;
// 判断当前窗口是否要关闭
if (bTerminating)
SendMessage (hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L) ;
return TRUE ;
case MM_WOM_OPEN:
// 启用和禁用按钮
EnableWindow (GetDlgItem (hwnd, IDC_RECORD_BEG), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_RECORD_END), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_BEG), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_PAUSE), TRUE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_END), TRUE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_REP), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_REV), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_SPEED), FALSE) ;
SetFocus (GetDlgItem (hwnd, IDC_PLAY_END)) ;
// 设置波形数据头
pWaveHdr1->lpData = pSaveBuffer ;
pWaveHdr1->dwBufferLength = dwDataLength ;
pWaveHdr1->dwBytesRecorded = 0 ;
pWaveHdr1->dwUser = 0 ;
// WHDR_BEGINLOOP 缓冲区是一个循环的第一个缓冲区
// WHDR_ENDLOOP 缓冲区是一个循环的最后一个缓冲区
pWaveHdr1->dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP ;
pWaveHdr1->dwLoops = dwRepetitions ;
pWaveHdr1->lpNext = NULL ;
pWaveHdr1->reserved = 0 ;
// 准备数据
waveOutPrepareHeader (hWaveOut, pWaveHdr1, sizeof (WAVEHDR)) ;
// 发送输入给设备
waveOutWrite (hWaveOut, pWaveHdr1, sizeof (WAVEHDR)) ;
bEnding = FALSE ;
bPlaying = TRUE ;
return TRUE ;
// 停止播放,或播放完毕时收到此消息
case MM_WOM_DONE:
// 清除数据
waveOutUnprepareHeader (hWaveOut, pWaveHdr1, sizeof (WAVEHDR)) ;
// 关闭设备,发送MM_WOM_CLOSE消息
waveOutClose (hWaveOut) ;
return TRUE ;
case MM_WOM_CLOSE:
// 启用和禁用按钮,设置焦点
EnableWindow (GetDlgItem (hwnd, IDC_RECORD_BEG), TRUE) ;
EnableWindow (GetDlgItem (hwnd, IDC_RECORD_END), TRUE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_BEG), TRUE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_PAUSE), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_END), FALSE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_REV), TRUE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_REP), TRUE) ;
EnableWindow (GetDlgItem (hwnd, IDC_PLAY_SPEED), TRUE) ;
SetFocus (GetDlgItem (hwnd, IDC_PLAY_BEG)) ;
SetDlgItemText (hwnd, IDC_PLAY_PAUSE, TEXT ("Pause")) ;
bPaused = FALSE ;
dwRepetitions = 1 ;
bPlaying = FALSE ;
// 如果点击了反向播放,则重置波形数据
if (bReverse)
{
ReverseMemory (pSaveBuffer, dwDataLength) ;
bReverse = FALSE ;
}
if (bTerminating)
SendMessage (hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L) ;
return TRUE ;
case WM_SYSCOMMAND:
switch (LOWORD (wParam))
{
case SC_CLOSE:
// 必须要先停止设备,才能正常关闭设备
if (bRecording)
{
bTerminating = TRUE ;
bEnding = TRUE ;
waveInReset (hWaveIn) ;
return TRUE ;
}
if (bPlaying)
{
bTerminating = TRUE ;
bEnding = TRUE ;
waveOutReset (hWaveOut) ;
return TRUE ;
}
free (pWaveHdr1) ;
free (pWaveHdr2) ;
free (pSaveBuffer) ;
EndDialog (hwnd, 0) ;
return TRUE ;
}
break ;
}
return FALSE ;
}