22.1 Windows和多媒体
22.1.1 Windows中支持多媒体功能的API
(1)底层接口:如波形音频输入、输出函数waveIn和waveOut前缀开头
MIDI输出设备midiOut函数
(2)高层接口:
①以mci为前缀的7个函数。mci本身有两种,一种是向MCI发送消息。一种是向MCI发送文本字符串。
②MessageBeep和PlaySound等函数。
22.1.2 TESTMCI程序
效果图
/*--------------------------------------------------------- TESTMCI.C —— MCI Command String Tester (c)Charles Petzold,1998 ---------------------------------------------------------*/ #include <Windows.h> #include "resource.h" #pragma comment(lib,"WINMM.lib") //须用到WINMM.DLL的导入表 #define ID_TIMER 1 BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM); TCHAR szAppName[] = TEXT("TestMci"); 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; } BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndEdit; int iCharBeg, iCharEnd,iLineBeg,iLineEnd,iLine,iLength,iChar; TCHAR szCommand[1024], szReturn[1024], szError[1024],szBuffer[24]; MCIERROR error; RECT rect; switch (message) { case WM_INITDIALOG: //将窗口置于屏幕中心 GetWindowRect(hwnd, &rect); SetWindowPos(hwnd, NULL, (GetSystemMetrics(SM_CXSCREEN) - rect.right + rect.left) / 2, (GetSystemMetrics(SM_CYSCREEN) - rect.bottom + rect.top) / 2, 0, 0, SWP_NOZORDER | SWP_NOSIZE); hwndEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT); SetFocus(hwndEdit); return FALSE; //手动设置焦点,应返回FALSE。否则会设在第1个拥有WS_TABSTOP控件上 case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: //计算文本框中被选中的开始和结束的行数 SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&iCharBeg, (LPARAM)&iCharEnd);//开始位置和结束位置 iLineBeg = SendMessage(hwndEdit, EM_LINEFROMCHAR, iCharBeg, 0); iLineEnd = SendMessage(hwndEdit, EM_LINEFROMCHAR, iCharEnd, 0); //遍历所有选中的行(如果没有选中的,则默认会读取最后一行文本) for (iLine = iLineBeg; iLine<=iLineEnd; iLine++) { //每次循环获取第iLine行文本,并在最末尾加上\0结束,如果是空行,则跳过。 //缓冲区第1个字为缓冲区字符个数(含\0),EM_GETLINE消息要求的 *(WORD*)szCommand = sizeof(szCommand) / sizeof(TCHAR); iLength = SendMessage(hwndEdit, EM_GETLINE, iLine, (LPARAM)szCommand); szCommand[iLength] = ‘\0‘; if (iLength == 0) continue; //跳过空行 //发送MCI命令 /* MCIERROR mciSendString(// 可用mciGetErrorString获得错误的文本描述 LPCTSTR lpszCommand, // 指向以null结尾的命令字符串:”命令 设备[ 参数]” LPTSTR lpszReturnString,// 指向接收返回信息的缓冲区,为NULL时不返回信息 UINT cchReturn, // 上述缓冲区的大小 HANDLE hwndCallback);//在命令串中含notify时,它指定一个回调窗口的句柄,一般为NULL */ error = mciSendString(szCommand, szReturn, sizeof(szReturn) / sizeof(TCHAR), hwnd); //显示返回的消息文本 SetDlgItemText(hwnd, IDC_RETURN_STRING, szReturn); //显示错误信息 mciGetErrorString(error, szError, sizeof(szError) / sizeof(TCHAR)); SetDlgItemText(hwnd, IDC_ERROR_STRING, szError); } //将插入符(Caret)放在选定行中最后一行的后面 iChar = SendMessage(hwndEdit, EM_LINEINDEX, iLineEnd, 0); //EM_LINEINDEX获取第iLineEnd行 //第1个字符的位置索引 iChar += SendMessage(hwndEdit, EM_LINELENGTH, iCharEnd, 0); //获取指定位置的字符(iCharEnd)所在行的字符个数 //iChar指定该行最后一个字符的后面。 SendMessage(hwndEdit, EM_SETSEL, iChar, iChar); //选择该行最后一个字符 //插入回车换行符 SendMessage(hwndEdit, EM_REPLACESEL, FALSE, (LPARAM)TEXT("\r\n")); SetFocus(hwndEdit); return TRUE; case IDCANCEL: EndDialog(hwnd, 0); return TRUE; case IDC_MAIN_EDIT: //编辑框消息 if (HIWORD(wParam) == EN_ERRSPACE) //请求的空间无法得到满足时 { MessageBox(hwnd, TEXT("Error control out of space."), szAppName, MB_OK | MB_ICONINFORMATION); return TRUE; } break; } break; /* lParam = (LONG) lDevID;// wParam = (WPARAM) wFlags: 1、MCI_NOTIFY_ABORTED 2、MCI_NOTIFY_FAILURE 3、MCI_NOTIFY_SUCCESSFUL 4、MCI_NOTIFY_SUPERSEDED */ case MM_MCINOTIFY: EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_MESSAGE), TRUE); wsprintf(szBuffer, TEXT("Device ID = %i"), lParam); SetDlgItemText(hwnd, IDC_NOTIFY_ID, szBuffer); EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_ID), TRUE); EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_SUCCESSFUL), wParam & MCI_NOTIFY_SUCCESSFUL); EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_SUPERSEDED), wParam & MCI_NOTIFY_SUPERSEDED); EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_ABORTED), wParam & MCI_NOTIFY_ABORTED); EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_FAILURE), wParam & MCI_NOTIFY_FAILURE); SetTimer(hwnd, ID_TIMER, 5000, NULL); return TRUE; case WM_TIMER: KillTimer(hwnd, ID_TIMER); EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_MESSAGE), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_ID), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_SUCCESSFUL), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_SUPERSEDED), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_ABORTED), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_FAILURE), FALSE); return TRUE; case WM_SYSCOMMAND: switch (LOWORD(wParam)) { case SC_CLOSE: EndDialog(hwnd, 0); return TRUE; } break; } return FALSE; }
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 TestMci.rc 使用 // #define IDC_MAIN_EDIT 1001 #define IDC_NOTIFY_ID 1002 #define IDC_NOTIFY_MESSAGE 1003 #define IDC_NOTIFY_SUCCESSFUL 1004 #define IDC_NOTIFY_ABORTED 1005 #define IDC_NOTIFY_FAILURE 1006 #define IDC_NOTIFY_SUPERSEDED 1007 #define IDC_ERROR_STRING 1008 #define IDC_RETURN_STRING 1009 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1006 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
//TestMci.rc
// Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // 中文(简体,中国) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Dialog // TESTMCI DIALOGEX 0, 0, 279, 290 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "MCI Tester" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "OK",IDOK,61,266,50,14 PUSHBUTTON "Close",IDCANCEL,146,266,50,14 EDITTEXT IDC_MAIN_EDIT,16,15,248,101,ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL LTEXT "Return String:",IDC_STATIC,19,118,46,8 EDITTEXT IDC_ERROR_STRING,144,130,120,67,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_GROUP | NOT WS_TABSTOP LTEXT "Error String:",IDC_STATIC,145,119,40,8 EDITTEXT IDC_RETURN_STRING,18,129,120,66,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_GROUP | NOT WS_TABSTOP GROUPBOX "MM_MCINOTIFY message",IDC_STATIC,18,199,247,62 LTEXT "",IDC_NOTIFY_ID,30,214,228,8 LTEXT "MCI_NOTIFY_SUCCESSFUL",IDC_NOTIFY_SUCCESSFUL,34,230,88,8,WS_DISABLED LTEXT "MCI_NOTIFY_ABORTED",IDC_NOTIFY_ABORTED,163,230,78,8,WS_DISABLED LTEXT "MCI_NOTIFY_FAILURE",IDC_NOTIFY_FAILURE,163,244,74,8,WS_DISABLED LTEXT "MCI_NOTIFY_SUPERSEDED",IDC_NOTIFY_SUPERSEDED,34,244,89,8,WS_DISABLED END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN "TESTMCI", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 272 TOPMARGIN, 7 BOTTOMMARGIN, 283 END END #endif // APSTUDIO_INVOKED #endif // 中文(简体,中国) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
22.2 波形音频
22.2.1 声音与波形
(1)正弦波
振幅:指声音强度
频率:指音调(人类耳朵从低音调到高音调的区间范围为20Hz~20 000Hz)
(2)人类对频率的感知是和频率的对数成正比的——频率翻倍定义为八度
(3)傅里叶级数和正弦波谐波:任何周期性的波形,都可以分解为多个正弦波,它们的频率关系是整数倍数。基波的频率代表正弦波的频率,基波也被称为一次谐波,还有二次、三次等谐波。
22.2.2 采样率和采样的大小
(1)采样率:指每秒对波形采集的样本数量,必须是声音最高频率的2倍。这被称为“奈奎斯将频率”。如人耳能听到高达20kHz的声音,为了获取人类可以听到的整个音频范围,采样率为40kHz(是声音最高频率的2倍),但考虑到经过低通滤波后有衰减效应,所以提高10%左右,即44kHz。一般取44.1kHz。
(2)采样大小:单位是位(bit)——用于表示声音的强度,决定了最小的声音和最大声音之间的差别。
以分贝来表示——dB = 20*log(A1/A2),其中A1和A2是两个声音的振幅。
①采样大小为1位,即A1/A2 =1,则动态范围dB=0,因为只有一个振幅。
②采样大小为8位,即A1/A2 =256,动态范围dB= 20*log(256),即48分贝。其范围相当于一个安静的房间与一台运行着的电动机之间的区别。
③采样大小为16位时,产生的动态范围为dB=20*log(65536),即96分贝,这个范围非常接近听力最低阈值和产生痛感阈值之间的区别。注意,立体声是双声道的,每个声道采样大小为16位。
(3)计算未压缩音频所需占用的空间:声音长度*采样率*采样大小/8 *声道数 (单位:字节)
22.2.3 用软件生成正弦波——WaveOutXXX波形输出设备函数的使用
22.2.3.1 程序结构
(1)本程序生成20Hz~5000Hz的正弦波
(2)在脉冲编码时,采样率是一个常数,本例采用11025Hz,设要生成的正弦波的频率为X,则正弦波的每个周期内采集的样本数量为11025/x。由于每个周期的样本数量可能是小数,这样每个周期末尾都不连续。所以不能用此方法来生成正弦波。
(3)正确的方法:——相位角。第1个样本为0度,递增值为正弦波频率*2π/采样率。如用11025Hz的采样率生成1000Hz的正弦波,则每个周期大约11个样本,用这11个样本的正弦值来表示每个样本的位数(振幅)。
22.2.3.2程序用到函数或结构体
(1)WAVEFORMATEX结构体
字段 |
含义 |
WORD wFormatTag |
指定格式类型; 默认 WAVE_FORMAT_PCM = 1 |
WORD nChannels |
指出波形数据的通道数; 单声道为 1, 立体声为 2 |
DWORD nSamplesPerSec |
指定样本采样率(每秒的样本数) |
DWORD nAvgBytesPerSec |
指定数据传输的平均速率(每秒的字节数) PCM编码:其值等于nSamplesPerSec*nBlockAlign |
WORD nBlockAlign |
指定块对齐, 块对齐是数据的最小单位。 PCM编码:其值等于nChannels*wBitsPerSample/8(单位字节),即每个样本占用的字节总数,16 位立体声 PCM 的块对齐是 4 字节(每个样本2字节, 2个通道) |
WORD wBitsPerSample |
采样大小(字节),等于8位或16位 |
WORD cbSize |
附加信息大小; PCM 格式没这个字段,可设为0。(PCM:脉冲编码调制) |
(2)waveOutOpen函数介绍
参数 |
含义 |
LPHWAVEOUT phwo |
指向接收波形音频输出装置的句柄。如果fdwFlags设定为WAVE_FORMAT_QUERY时,该参数可为NULL |
UINT uDeviceID |
①将要被打开的波形音频输出装置的ID,该值是个索引值,范围从0至系统中波形输出设备的数量减1. ②波形输出设备数量可用WaveOutGetNumDevs查询。 ③如果该值设为WAVE_MAPPER(-1),则会自动选择,一般会选择控制面板的多媒体程序的【音频】选项卡中【首选设备】中指定的设备,如果无法满足需求,会自动选择另一个设备。 |
LPWAVEFORMATEX pwfx |
指向一个WAVEFORMATEX结构体的指针 |
DWORD dwCallback |
指明接收波形输出消息的窗口句柄或回调函数。注意:回调函数是在中断时间内访问的, 必须在 DLL 中。要访问的数据都必须是在固定的数据段中。 |
DWORD dwCallbackInstance |
如果dwCallBack指明了使用回调函数,则该参数可以指定要传递给回调函数额外的数据。 |
DWORD fdwFlags |
CALLBACK_WINDOW、CALLBACK_FUNCTION:指定第4个参数的类型。 CALLBACK_EVENT:指定dwCallback为事件句柄 CALLBACK_THREAD:指定dwCallback为线程ID WAVE_ALLOWSYNC:当是同步设备时必须指定 WAVE_FORMAT_QUERY:检查设备是否可以打开,而不实际去打开它。 |
【返回值】
①MMSYSERR_NOERROR = 0; {成功时}
②MMSYSERR_BADDEVICEID = 2; {设备ID超界}
③MMSYSERR_ALLOCATED = 4; {指定的资源已被分配}
④MMSYSERR_NODRIVER = 6; {没有安装驱动程序}
⑤MMSYSERR_NOMEM = 7; {不能分配或锁定内存}
⑥WAVERR_BADFORMAT = 32; {设备不支持请求的波形格式}
说明:
①函数调用后立即返回
②如果选择窗口句柄为接收波形输出消息,则会收到WM_WOM_OPEN消息,wParam为波形输出设备句柄,lParam为保留值(无用)
(3)WaveOutPrepareHeader函数及WAVEHDR结构体
①WaveOutPrepareHeader函数:准备波形数据,可避免WAVEHDR结构体和缓冲区从内存被交换到磁盘去。
②WAVEHDR结构体
字段 |
含义 |
LPSTR lpData; |
存储声音数据的缓冲区地址,要在堆里的,不能是函数的局部变量,MM_WOM_DONE消息的处理函数中还要用到。 |
DWORD dwBufferLength |
上述缓冲区的大小 |
DWORD dwBytesRecorded |
当设备用于录音时,标志已经录入的数据长度 |
DWORD dwUser |
给应用程序用的 |
DWORD dwFlags; |
波形数据的缓冲区的属性 WHDR_BEGINLOOP:指明该缓冲区是每次循环的第一个缓冲区。 WHDR_ENDLOOP:指明该缓冲区是每次循环的最后一个缓冲区。 WHDR_DONE:指明缓冲区数据播放完毕后,被设备驱动程序设为该值。 |
DWORD dwLoops |
循环播放的次数 |
struct wavehdr_tag * lpNext |
保留值 |
DWORD reserved |
保留值 |
(4)waveOutWrite函数——向指定的波输形输出设备传入数据,真正开始播放声音
★播放完后发送一条:MM_WOM_DONE消息,wParam为波形输出设备的句柄,lParam为指向WAVEHDR结构的指针。
(5)waveOutReset函数——停止声音播放,然后将当前位置置0,所有等待播放的数据被标志为Done,并发送MM_WOM_DONE消息。
①waveOutReset不是立即返回的函数,要等待驻留在波形输出设备中的数据处理完才返回。
②在调用到返回之间的这段时间内,禁止任何线程对处理reset中的hWaveOut调用,否则会造成死锁。
(6)waveOutClose函数——发送MM_WOM_CLOSE消息
(7)waveOutUnprepareHeader——清除由 waveOutPrepareHeader 完成的准备。
//SineWave.c
#include <Windows.h> #include <math.h> #include "resource.h" #pragma comment(lib,"WINMM.lib") #define SAMPLE_RATE 11025 #define FREQ_MIN 20 #define FREQ_MAX 5000 #define FREQ_INIT 440 #define PI 3.1415926 #define OUT_BUFFER_SIZE 4096 TCHAR szAppName[] = TEXT("SineWave"); BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nCmdShow) { 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; //用相位角来生成正弦波声音数据,声音强度(振幅)从0-254 for (i= 0; i<OUT_BUFFER_SIZE; i++) { pBuffer[i] = (BYTE)(127+127*sin(fAngle)); //相位增量为:正弦波频率*2π/采样率 fAngle += 2 * PI * iFreq / SAMPLE_RATE; if (fAngle>2 * PI) fAngle -= 2 * PI; //注意这里不能重置为0,因为要让声音连续。 } } BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static BOOL bShutOff, bClosing; static HWND hwndScroll; static int iFreq = FREQ_INIT; static PBYTE pBuffer1, pBuffer2; //用双缓冲播放声音 static PWAVEHDR pWaveHdr1, pWaveHdr2; static WAVEFORMATEX waveformat; int iDummy; static HWAVEOUT hWaveOut; RECT rect; switch (message) { case WM_INITDIALOG: //将窗口置于屏幕中央 GetWindowRect(hwnd, &rect); SetWindowPos(hwnd, NULL, (GetSystemMetrics(SM_CXSCREEN) - rect.right + rect.left) / 2, (GetSystemMetrics(SM_CYSCREEN) - rect.bottom + rect.top) / 2, rect.right - rect.left, rect.bottom - rect.top, SWP_SHOWWINDOW); hwndScroll = GetDlgItem(hwnd, IDC_SCROLL); SetScrollRange(hwndScroll, SB_CTL, FREQ_MIN, FREQ_MAX, FALSE); SetScrollPos(hwndScroll, SB_CTL, FREQ_INIT, TRUE); SetDlgItemInt(hwnd, IDC_TEXT, FREQ_INIT, FALSE); //SendMessage(hwnd, DM_SETDEFID, (WPARAM)IDC_SCROLL, 0); //测试用的,改变对话框回车键默认行为 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;//降低1个八度 case SB_PAGERIGHT: iFreq *= 2; break;//升高1个八度 case SB_THUMBTRACK: iFreq = HIWORD(wParam);break; case SB_TOP: GetScrollRange(hwndScroll, SB_CTL, &iFreq, &iDummy); //将最小值放入iFreq,最大放入iDummy break; case SB_BOTTOM: GetScrollRange(hwndScroll, SB_CTL, &iDummy, &iFreq); //将最大值放入iFreq break; } iFreq = max(FREQ_MIN, min(FREQ_MAX, iFreq)); SetScrollPos(hwndScroll, SB_CTL, iFreq, TRUE); SetDlgItemInt(hwnd, IDC_TEXT, iFreq, FALSE); return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_ONOFF: //如果是要打开波形音频,则hWaveOut==NULL if (hWaveOut == NULL) { //为2个声音头结构和声音数据分配内存 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;//nSamplesPerSec*nBlockAlign waveformat.nBlockAlign = 1; //每个样本所占的字节数,nChannels*wBitsPerSample/8 waveformat.wBitsPerSample = 8; //每个样本大小(位数)(单位:位) waveformat.cbSize = 0; //自动选择波形输出设备,并打开。消息发送指定的窗口 if (MMSYSERR_NOERROR != waveOutOpen(&hWaveOut , WAVE_MAPPER , &waveformat, (DWORD)hwnd, 0, CALLBACK_WINDOW )) { 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; //防止WAVEHDR和缓冲区从内存被交换到磁盘去 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; //防止WAVEHDR和缓冲区从内存被交换到磁盘去 waveOutPrepareHeader(hWaveOut, pWaveHdr2, sizeof(WAVEHDR)); } else //如果要关闭波形文件,则输出设备复位 { bShutOff = TRUE; waveOutReset(hWaveOut);//停止播放 } return TRUE; } break; case MM_WOM_OPEN: //调用waveOutOpen函数后,收到该消息 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; case MM_WOM_DONE: //当缓冲区数据播放完毕或waveOutReset时,收到该消息 if (bShutOff) { waveOutClose(hWaveOut); //按下Turn Off按钮 return TRUE; } //如果缓冲区的数据播放完毕,则 //生成新的声音数据,并发送给波输形输出设备 FillBuffer(((PWAVEHDR)lParam)->lpData, iFreq); waveOutWrite(hWaveOut, (PWAVEHDR)lParam, sizeof(WAVEHDR)); return TRUE; case MM_WOM_CLOSE: waveOutUnprepareHeader(hWaveOut, pWaveHdr1, sizeof(WAVEHDR)); waveOutUnprepareHeader(hWaveOut, pWaveHdr2, sizeof(WAVEHDR)); if (pWaveHdr1) free(pWaveHdr1); if (pWaveHdr2) free(pWaveHdr2); if (pBuffer1) free(pBuffer1); if (pBuffer2) 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; }
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 SineWave.rc 使用 // #define IDC_SCROLL 1001 #define IDC_TEXT 1002 #define IDC_ONOFF 1003 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1004 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
//SineWave.rc
// Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // 中文(简体,中国) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Dialog // SINEWAVE DIALOGEX 0, 0, 247, 62 STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Sine Wave Generator" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "Turn On",IDC_ONOFF,106,44,50,14 SCROLLBAR IDC_SCROLL,15,12,189,11,WS_TABSTOP RTEXT "440",IDC_TEXT,202,13,25,8 LTEXT "Hz",IDC_STATIC,229,13,9,8,WS_TABSTOP END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN "SINEWAVE", DIALOG BEGIN END END #endif // APSTUDIO_INVOKED #endif // 中文(简体,中国) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED