22,波形音频

人发出的是声波,而计算机是以数字形式工作的,所以计算机有必要提供一种将声音转换为数字,再把数字转换为声音的机制。


最常见的不压缩数据的方法被称为“脉冲编码调制”(pluse code modulation,PCM)

脉冲编码调制以固定周期频率对波形进行采样,通常每秒几万次。对于每个样品,需要测量波形的幅值,这样分割的很细的话,得到的很多个点再绘制起来就可以还原原来的波形了。模数转换器(analog-digital conventer,ADC)负责将振幅转换为数字,同样数模转换器(digital-to-analog,DAC)负责将数字转换为电子波形。

又由于播放出来的声音和之前输入的声音并不完全相同,因为这样产生的波形在高频部分具有尖锐的边缘,出于这个原因,播放的硬件往往在数模转换器之后再连接一个低通滤波器,该滤波器能剔除高频率,产生平滑的波形,在输入端,低通滤波器防止在ADC之前。

脉冲编码调制有两个参数:采样率(即每秒测量波形幅值的次数)和采样大小(即存储幅值水平的位数),然而也并不是采样率越高,采样大小越大越好,因为那种情况可能超越人类感知的范围了。对于采样大小windows支持8位和16位,分别表示一个字节和两个字节。如果要计算未压缩音频所需空间,只要将声音的长度秒数乘以采样率即可。


下面是函数生成人体感知的波形数据,然后写入波形音频硬件播放:


1.先开辟两块波形数据缓存指针(PBYTE)和两块波形数据头部(PWAVEHDR),用于向波形音频输出硬件写入

2,定义波形音频的格式,指定那个音频格式,声道类型,采样率,采样大小等等

3,waveOutOpen函数打开波形音频播放硬件,该函数可得到波形音频播放硬件句柄,

4,将波形数据缓存和波形数据头部联系起来,同时调用waveOutPrepareHeader准备好数据头部,目的是在写入播放硬件时,只要先获得了波形数据,就可以只传数据头部即可

5.调用函数生成波形音频数据,填充缓存,waveOutWrite传入波形数据头部将数据写入波形音频输出硬件,即可播放。

6,释放开辟的内存


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(HWNF,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("unknow error occurs"),
			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;
		}

		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 turning on waveform,hwaveout is null

				if(hWaveOut==NULL)
				{
					//allocate memory for 2 headers and 2 buffers
					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;
					}

					//variable to indicate off button pressed

					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;
					
					//open waveform audio for ouput 

					//hWaveOut 用来获取音频输出设备的句柄,
					//就会产生MM_WOM_OPEN

					if(waveOutOpen(&hWaveOut,WAVE_MAPPER,&waveformat,
						(DWORD)hwnd,0,CALLBACK_WINDOW)       //(DWORD)hwnd 定义回调机制,CALL_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;
					}

					//set up header1 and prepare them 
					//该结构是为了通过api向波形音频设备写入数据

					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;

					waveOutPrepareHeader(hWaveOut,pWaveHdr2,sizeof(WAVEHDR));
				}
				else
				{
					//if turning off waveform ,reset waveform audio

					bShutOff=TRUE;
					waveOutReset(hWaveOut);
				}
				return TRUE;
			}
			break;

			//message generated from waveoutopen call

			case MM_WOM_OPEN:
				SetDlgItemText(hwnd,IDC_ONOFF,TEXT("Turn Off"));

				//send two buffer waveform output device
				//数据写入到波形输出硬件,真正开始播放声音

				FillBuffer(pBuffer1,iFreq);   //注意pBuffer是指针
				waveOutWrite(hWaveOut,pWaveHdr1,sizeof(WAVEHDR));

				FillBuffer(pBuffer2,iFreq);
				waveOutWrite(hWaveOut,pWaveHdr2,sizeof(WAVEHDR));
				return TRUE;

				//message generated when a buffer is finished

			case MM_WOM_DONE:
				if(bShutOff)
				{
					waveOutClose(hWaveOut);   //又产生MM_WOM_CLOSE
					return TRUE;
				}

				//fill and send out a new buffer

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

				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);
				}
				break;
	}
	return FALSE;
}

resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by SineWave.rc
//
#define IDC_STATIC			-1
#define IDC_SCROLL                      1000
#define IDC_TEXT			1001
#define IDC_ONOFF                       1002

// 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         1003
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

新建一个resource.rc


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值