[Win32]waveIn*/waveOut*录音和播放-Ocrosoft
浙江财经大学
信工学院ACM集训队

[Win32]waveIn*/waveOut*录音和播放

本文由 Ocrosoft 于 2018-07-05 22:25:02 发表

一直不能开始录音,结束也结束不了,最后发现是把pWaveHdr2写成了pWaveHdr1,_(:з」∠)_

跟之前一样,要先Open设备,PrepareHeader,AddBuffer。结束要UnPrepareHeader,free缓冲区。

主要就是waveform的一些参数设置。

RECORD1.C

#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 hInstace, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	// 使用DIALOG作为主窗体
	if (-1 == DialogBox(hInstace, TEXT("Record"), NULL, DlgProc))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
	}

	return 0;
}

// 翻转BYTE数组
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 ID_RECORD_BEG:
			// 两个缓冲区分配1.6KB内存
			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;
			}

			// PCM格式
			waveform.wFormatTag = WAVE_FORMAT_PCM;
			waveform.nChannels = 1;
			// 11025采样率
			waveform.nSamplesPerSec = 11025;
			waveform.nAvgBytesPerSec = 11025;
			waveform.nBlockAlign = 1;
			waveform.wBitsPerSample = 8;
			waveform.cbSize = 0;

			// 把一个MM_WIM_OPEN给放入消息队列
			// 成功返回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 ID_RECORD_END:
			bEnding = TRUE;
			// 将一个MM_WIM_DATA消息放入消息队列
			waveInReset(hWaveIn);

			return TRUE;

		case ID_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;

			// 打开音频输出,成功返回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 ID_PLAY_PAUSE:
			if (!bPaused)
			{
				waveOutPause(hWaveOut);
				SetDlgItemText(hwnd, ID_PLAY_PAUSE, TEXT("Resume"));
				bPaused = TRUE;
			}
			else
			{
				waveOutRestart(hWaveOut);
				SetDlgItemText(hwnd, ID_PLAY_PAUSE, TEXT("Pause"));
				bPaused = FALSE;
			}

			return TRUE;

			// 停止播放
		case ID_PLAY_END:
			bEnding = TRUE;
			// 发送MM_WOM_CLOSE消息
			waveOutReset(hWaveOut);

			return TRUE;

			// 倒放
		case ID_PLAY_REV:
			bReverse = TRUE;
			ReverseMemory(pSaveBuffer, dwDataLength);

			SendMessage(hwnd, WM_COMMAND, ID_PLAY_BEG, 0);

			return TRUE;

			// 重复播放
		case ID_PLAY_REP:
			// 重复标记
			dwRepetitions = -1;
			SendMessage(hwnd, WM_COMMAND, ID_PLAY_BEG, 0);

			return TRUE;

			// 两倍采样频率、两倍每秒字节数播放
		case ID_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;

	case MM_WIM_OPEN:
		// realloc在此处与free+malloc功能类似
		pSaveBuffer = realloc(pSaveBuffer, 1);

		// 启用禁用按钮
		EnableWindow(GetDlgItem(hwnd, ID_RECORD_BEG), FALSE);
		EnableWindow(GetDlgItem(hwnd, ID_RECORD_END), TRUE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_BEG), FALSE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_PAUSE), FALSE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_END), FALSE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_REV), FALSE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_REP), FALSE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_SPEED), FALSE);
		SetFocus(GetDlgItem(hwnd, ID_RECORD_END));

		// 添加缓冲区
		waveInAddBuffer(hWaveIn, pWaveHdr1, sizeof(WAVEHDR));
		waveInAddBuffer(hWaveIn, pWaveHdr2, sizeof(WAVEHDR));

		bRecording = TRUE;
		bEnding = FALSE;
		dwDataLength = 0;
		// 开始录音
		waveInStart(hWaveIn);

		return TRUE;

		// WaveInReset或者缓冲区溢出(超过pBuffer的1.6K后)收到DATA消息
	case MM_WIM_DATA:
		// 追加新录音的内存大小
		pNewBuffer = realloc(pSaveBuffer, dwDataLength + ((PWAVEHDR)lParam)->dwBytesRecorded);

		// realloc失败(内存不足)
		if (pNewBuffer == NULL)
		{
			waveInClose(hWaveIn);
			MessageBeep(MB_ICONEXCLAMATION);
			MessageBox(hwnd, szMemError, szAppName, MB_ICONEXCLAMATION | MB_OK);

			return TRUE;
		}

		// realloc可能导致内存地址变化
		// 使用pNewBuffer目的是失败后停止,保存之前成功的录音
		pSaveBuffer = pNewBuffer;
		// 将新的录音追加到原来的录音后面
		CopyMemory(pSaveBuffer + dwDataLength, ((PWAVEHDR)lParam)->lpData, ((PWAVEHDR)lParam)->dwBytesRecorded);

		// 计算新的长度
		dwDataLength += ((PWAVEHDR)lParam)->dwBytesRecorded;

		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, ID_RECORD_BEG), TRUE);
		EnableWindow(GetDlgItem(hwnd, ID_RECORD_END), FALSE);
		SetFocus(GetDlgItem(hwnd, ID_RECORD_BEG));

		// 检查是否有录音,调整UI
		if (dwDataLength > 0)
		{
			EnableWindow(GetDlgItem(hwnd, ID_PLAY_BEG), TRUE);
			EnableWindow(GetDlgItem(hwnd, ID_PLAY_PAUSE), FALSE);
			EnableWindow(GetDlgItem(hwnd, ID_PLAY_END), FALSE);
			EnableWindow(GetDlgItem(hwnd, ID_PLAY_REV), TRUE);
			EnableWindow(GetDlgItem(hwnd, ID_PLAY_REP), TRUE);
			EnableWindow(GetDlgItem(hwnd, ID_PLAY_SPEED), TRUE);
			SetFocus(GetDlgItem(hwnd, ID_PLAY_BEG));
		}

		bRecording = FALSE;

		if (bTerminating)
			SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L);

		return TRUE;

	case MM_WOM_OPEN:
		EnableWindow(GetDlgItem(hwnd, ID_RECORD_BEG), FALSE);
		EnableWindow(GetDlgItem(hwnd, ID_RECORD_END), FALSE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_BEG), FALSE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_PAUSE), TRUE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_END), TRUE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_REV), FALSE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_REP), FALSE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_SPEED), FALSE);
		SetFocus(GetDlgItem(hwnd, ID_RECORD_END));

		// 只有一段音频,不使用双缓冲
		pWaveHdr1->lpData = pSaveBuffer;
		pWaveHdr1->dwBufferLength = dwDataLength;
		pWaveHdr1->dwBytesRecorded = 0;
		pWaveHdr1->dwUser = 0;
		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;

		// waveOutReset()或播放完毕触发
	case MM_WOM_DONE:
		waveOutUnprepareHeader(hWaveOut, pWaveHdr1, sizeof(WAVEHDR));
		waveOutClose(hWaveOut);

		return TRUE;

	case MM_WOM_CLOSE:
		EnableWindow(GetDlgItem(hwnd, ID_RECORD_BEG), TRUE);
		EnableWindow(GetDlgItem(hwnd, ID_RECORD_END), TRUE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_BEG), TRUE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_PAUSE), FALSE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_END), FALSE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_REV), TRUE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_REP), TRUE);
		EnableWindow(GetDlgItem(hwnd, ID_PLAY_SPEED), TRUE);
		SetFocus(GetDlgItem(hwnd, ID_PLAY_BEG));

		SetDlgItemText(hwnd, ID_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;
				// 尝试停止录音
				waveOutReset(hWaveOut);
				
				return TRUE;
			}

			free(pWaveHdr1);
			free(pWaveHdr2);
			free(pSaveBuffer);
			EndDialog(hwnd, 0);
			return TRUE;
		}
		break;
	}
	return FALSE;
}

resource.h

#define ID_RECORD_BEG					1000
#define ID_RECORD_END					1001
#define ID_PLAY_BEG					1002
#define ID_PLAY_PAUSE					1003
#define ID_PLAY_END					1004
#define ID_PLAY_REV					1005
#define ID_PLAY_REP					1006
#define ID_PLAY_SPEED					1007

RECORD1.RC

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

RECORD DIALOGEX 100, 100, 152, 74
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Waveform Audio Recorder"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    PUSHBUTTON      "Record",ID_RECORD_BEG,28,8,40,14
    PUSHBUTTON      "End",ID_RECORD_END,76,8,40,14,WS_DISABLED
    PUSHBUTTON      "Play",ID_PLAY_BEG,8,30,40,14,WS_DISABLED
    PUSHBUTTON      "Pause",ID_PLAY_PAUSE,56,30,40,14,WS_DISABLED
    PUSHBUTTON      "End",ID_PLAY_END,104,30,40,14,WS_DISABLED
    PUSHBUTTON      "Reverse",ID_PLAY_REV,8,52,40,14,WS_DISABLED
    PUSHBUTTON      "Repeat",ID_PLAY_REP,56,52,40,14,WS_DISABLED
    PUSHBUTTON      "Speedup",ID_PLAY_SPEED,104,52,40,14,WS_DISABLED
END

欢迎转载,请保留出处与链接。Ocrosoft » [Win32]waveIn*/waveOut*录音和播放

点赞 (1)or拍砖 (1)

评论 抢沙发

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