[Win32]波形音频(WaveOut*)-Ocrosoft
浙江财经大学
信工学院ACM集训队

[Win32]波形音频(WaveOut*)

本文由 Ocrosoft 于 2018-06-24 21:35:37 发表

前面有一节是MCI函数,由于没有CD-ROM,跳过。

WaveOut开头的函数可以用来打开波形音频设备(声卡)、播放波形等。

WaveOut*函数不用来播放WAV文件(有PlaySound),是一个低级波形音频函数,利用这些函数可以程序自己生成的波形,例如Sin波。

PS:tan波好难听

此外,所有多媒体函数都需要在项目属性-链接器-输入,添加winmm.lib,项目默认不包含。

SINEWAVE/resource.h

#define IDC_STATIC                      -1
#define IDC_SCROLL                      1000
#define IDC_TEXT                        1001
#define IDC_ONOFF                       1002

SINEWAVE/SINEWAVE.rc

/////////////////////////////////////////////////////////////////////////////
// Dialog

SINEWAVE DIALOG DISCARDABLE  100, 100, 200, 50
STYLE WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Sine Wave Generator"
FONT 8, "MS Sans Serif"
BEGIN
	SCROLLBAR       IDC_SCROLL, 8, 8, 150, 12
	RTEXT           "440", IDC_TEXT, 160, 10, 20, 8
	LTEXT           "Hz", IDC_STATIC, 182, 10, 12, 8
	PUSHBUTTON      "Turn On", IDC_ONOFF, 80, 28, 40, 14
END

/////////////////////////////////////////////////////////////////////////////

SINEWAVE/sinewave.c

#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);
		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));

		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;

				// 打开波形音频输出设备
				// hWaveOut,句柄
				// [0-设备数量),WAVE_MAPPER(-1)表示自动选择
				// 窗口句柄(或DLL中的回调函数)
				// 参数三使用回调函数时,指定定义的数据
				// CALLBACK_WINDOW,表示第三个参数是窗口句柄,CALLBACK_FUNCTION,表示参数三是回调函数
				// 成功返回MMSYSERR_NOERROR,否则返回具体错误
				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;
				}

				// 注册两个header
				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;

				// 停止播放声音,并发送一个MM_WOM_DOWN消息
				waveOutReset(hWaveOut);
			}
			return TRUE;
		}
		break;

	case MM_WOM_OPEN:
		// waveOutOpen会在消息队列中放一个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;

	case MM_WOM_DONE:
		// 一个缓冲区播放完后,程序收到MM_WOM_DONW消息,
		// lParam是HEADER结构,->lpData是播放完的缓冲区
		// 计算并填充缓冲区,并发送,保持播放不间断
		if (bShutOff)
		{
			// 调用会发送一个MM_WOM_CLOSE消息
			// 如果没有停止播放,调用会失败,\
				必须调用waveOutReset发送MM_WOM_DONE消息并在其消息处理时被调用
			waveOutClose(hWaveOut);
			return TRUE;
		}

		FillBuffer(((PWAVEHDR)lParam)->lpData, iFreq);
		waveOutWrite(hWaveOut, (PWAVEHDR)lParam, sizeof(WAVEHDR));
		return TRUE;

	case MM_WOM_CLOSE:
		// 卸载两个HEADER(缓冲区)
		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:
			// 没有停止播放直接关闭窗口时\
				先停止播放,进行卸载操作,在MM_WOM_CLOSE中关闭对话框
			if (hWaveOut != NULL)
			{
				bShutOff = TRUE;
				bClosing = TRUE;

				waveOutReset(hWaveOut);
			}
			else
				EndDialog(hwnd, 0);

			return TRUE;
		}
		break;
	}
	return FALSE;
}

欢迎转载,请保留出处与链接。Ocrosoft » [Win32]波形音频(WaveOut*)

点赞 (0)or拍砖 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址