www.pudn.com > WavePlayer_Final.rar > WavePlayerDlg.cpp
// WavePlayerDlg.cpp : implementation file
//
#include "stdafx.h"
#include "WavePlayer.h"
#include "WavePlayerDlg.h"
#include "fft.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define IDT_PROGRESS 128
#define IDT_FREQ 129
#define TIMESPICE 100
#define ABS(n) ((n) >= 0 ? (n) : -(n))
void ReadWave(CString sFile,UINT nSize,UINT nBytePerPoint,LPVOID pBuffer);
void DrawWave(CDC *pDC,CRect *pRect,UINT nSize,CString sFile,UINT nBytePerPoint);
/////////////////////////////////////////////////////////////////////////////
// CWavePlayerDlg dialog
//构造函数,初始化变量
CWavePlayerDlg::CWavePlayerDlg(CWnd* pParent /*=NULL*/)
: CDialog(CWavePlayerDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CWavePlayerDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
m_hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);
//以下初始化一些变量
m_sFile="";
m_bExtended=FALSE;
m_pBuffer=NULL;
m_pFreq=NULL;
m_bPlaying=FALSE;
}
//变量与控件关联,由向导生成
void CWavePlayerDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CWavePlayerDlg)
DDX_Control(pDX, IDOK, m_bnOK);
DDX_Control(pDX, IDC_PAUSE, m_bnPause);
DDX_Control(pDX, IDC_PROGRESS, m_slProgress);
DDX_Control(pDX, IDC_STOP, m_bnStop);
DDX_Control(pDX, IDC_PLAY, m_bnPlay);
DDX_Control(pDX, IDC_OPEN, m_bnOpen);
DDX_Control(pDX, IDC_DRAW, m_bnDraw);
DDX_Control(pDX, IDC_ABOUT, m_bnAbout);
DDX_Control(pDX, IDC_LISTWAVINFO, m_lWaveInfo);
//}}AFX_DATA_MAP
}
//消息映射宏,由向导生成
BEGIN_MESSAGE_MAP(CWavePlayerDlg, CDialog)
//{{AFX_MSG_MAP(CWavePlayerDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_OPEN, OnOpen)
ON_BN_CLICKED(IDC_PLAY, OnPlay)
ON_BN_CLICKED(IDC_STOP, OnStop)
ON_BN_CLICKED(IDC_PAUSE, OnPause)
ON_WM_DESTROY()
ON_WM_TIMER()
ON_BN_CLICKED(IDC_DRAW, OnDrawWave)
ON_BN_CLICKED(IDC_ABOUT, OnAbout)
//}}AFX_MSG_MAP
ON_MESSAGE(MM_MCINOTIFY,OnComplete)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CWavePlayerDlg message handlers
BOOL CWavePlayerDlg::OnInitDialog()
{
CDialog::OnInitDialog();
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
//初始化控件属性
GetDlgItem(IDC_PLAY)->EnableWindow(FALSE);
GetDlgItem(IDC_PAUSE)->EnableWindow(FALSE);
GetDlgItem(IDC_STOP)->EnableWindow(FALSE);
GetDlgItem(IDC_DRAW)->EnableWindow(FALSE);
m_slProgress.EnableWindow(FALSE);
//初始化波形图像的矩形
CRect rcBar;
m_slProgress.GetWindowRect(&rcBar);
m_rcWave=rcBar;
m_rcWave.bottom-=10;
m_rcWave.top=m_rcWave.bottom-170;
return TRUE; // return TRUE unless you set the focus to a control
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
//OnPaint
void CWavePlayerDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
if (m_bExtended)//若窗口已经展开
{
//则把内存DC中已经画好的波形图像贴到窗口合适的位置上,而不是每次再去画。
//这样可以防止闪烁
CRect rc;
m_slProgress.GetWindowRect(&rc);
ScreenToClient(&rc);
if (!m_bPlaying) //若正在播放则显示频谱
GetDC()->BitBlt(rc.left,rc.bottom+20,m_rcWave.Width(),m_rcWave.Height(),&m_dcBuffer,0,0,SRCCOPY);
else //否则显示波形
GetDC()->BitBlt(rc.left,rc.bottom+20,m_rcWave.Width(),m_rcWave.Height(),&m_dcBufferFreq,0,0,SRCCOPY);
}
CDialog::OnPaint();
}
}
HCURSOR CWavePlayerDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
//打开文件
void CWavePlayerDlg::OnOpen()
{
char szFilters[]=
"声音文件 (*.wav)|*.wav|";
CFileDialog dlg(TRUE,"wav","*.wav",
OFN_HIDEREADONLY,szFilters,this);
//调用“打开文件”对话框
if( dlg.DoModal()==IDOK)
{
CString sPathName = dlg.GetPathName(); //带路径的完整文件名
CString sFileName = dlg.GetFileTitle(); //文件名(不带路径)
if (sPathName==m_sFile) //若打开的是与当前文件相同的文件则不继续
return;
CString sTitle;
GetWindowText(sTitle);
sTitle="声音文件播放器--"+sFileName;
SetWindowText(sTitle); //更改窗口标题
m_sFile=sPathName; //将完整文件名保存在类的成员变量中
}
else
return;
GetInfo(); //读文件头
ReadWave(); //读声音波形
DrawWave(); //在内存DC中画中波形
if (m_bExtended)
{
OnDrawWave();
}
GetDlgItem(IDC_PLAY)->EnableWindow(TRUE); //激活“播放”按钮
GetDlgItem(IDC_DRAW)->EnableWindow(TRUE);
}
//从文件头中获取信息并显示在列表框中
void CWavePlayerDlg::GetInfo()
{
int i;
if (m_sFile!="") //若列表框中原来就有信息则将其清空
{
for (i=0;i<13;i++)
m_lWaveInfo.DeleteString(0);
}
//以下按格式读文件头
CFile fWave;
if (fWave.Open(m_sFile,CFile::modeRead,NULL))
{
fWave.Read(m_WaveFileHead.riff_id,sizeof(char)*4);
fWave.Read(&m_WaveFileHead.size0,sizeof(int));
fWave.Read(m_WaveFileHead.wave_fmt,sizeof(char)*8);
fWave.Read(&m_WaveFileHead.size1,sizeof(int));
fWave.Read(&m_WaveFileHead.fmttag,sizeof(short));
fWave.Read(&m_WaveFileHead.channel,sizeof(short));
fWave.Read(&m_WaveFileHead.sampl,sizeof(int));
fWave.Read(&m_WaveFileHead.bytepersecblockalign,sizeof(int));
fWave.Read(&m_WaveFileHead.blockalign,sizeof(short));
fWave.Read(&m_WaveFileHead.bitpersamples,sizeof(short));
fWave.Read(m_WaveFileHead.data,sizeof(char)*4);
fWave.Read(&m_WaveFileHead.datasize,sizeof(int));
//手动给几个字符串加上结束标志
m_WaveFileHead.riff_id[4]='\0';
m_WaveFileHead.wave_fmt[8]='\0';
m_WaveFileHead.data[4]='\0';
//以下代码是考虑到有一些声音文件头中“data”的位置不确定,所以手动去找
if (strcmp(m_WaveFileHead.data,"data")!=0)
{
do
{
char temp;
fWave.Read(&temp,sizeof(char));
if (temp=='d')
{
char temp3[4];
fWave.Read(&temp3,sizeof(char)*3);
temp3[3]=0;
if (strcmp(temp3,"ata")==0)
{
fWave.Read(&m_WaveFileHead.datasize,sizeof(int));
break;
}
else
{
fWave.Seek(-3,CFile::current);
continue;
}
}
}while (TRUE);
}
fWave.Close();
}
else
return;
//把得到的信息放入字符串数组中
CString sInfo[13];
sInfo[0].Format("%s",m_WaveFileHead.riff_id);
sInfo[1].Format("波形块的大小:%d",m_WaveFileHead.size0);
sInfo[2].Format("%s",m_WaveFileHead.wave_fmt);
sInfo[3].Format("格式格大小:%d",m_WaveFileHead.size1);
sInfo[4].Format("波形编码格式:%d",m_WaveFileHead.fmttag);
sInfo[5].Format("波形文件数据中的通道数:%d",m_WaveFileHead.channel);
sInfo[6].Format("波形文件的采样率:%d",m_WaveFileHead.sampl);
sInfo[7].Format("平均每秒波形音频所需要的记录的字节数:%d",m_WaveFileHead.bytepersecblockalign);
sInfo[8].Format("一个采样所需要的字节数:%d",m_WaveFileHead.blockalign);
sInfo[9].Format("声音文件数据的每个采样的位数:%d",m_WaveFileHead.bitpersamples);
sInfo[10].Format("%s",m_WaveFileHead.data);
sInfo[11].Format("Samples:%d",m_WaveFileHead.datasize);
sInfo[12].Format("长度:%4.2lf秒",m_fTime=(float)m_WaveFileHead.size0/m_WaveFileHead.bytepersecblockalign);
//把字符串数组的信息加入列表框
for (i=0;i<13;i++)
m_lWaveInfo.AddString(sInfo[i]);
}
void CWavePlayerDlg::OnPlay()
{
//调用mci函数打开并播放声音文件
MCI_OPEN_PARMS mciOpenParms;
MCI_PLAY_PARMS PlayParms;
mciOpenParms.dwCallback=0;
mciOpenParms.lpstrElementName=m_sFile;
mciOpenParms.lpstrDeviceType="waveaudio";
mciOpenParms.lpstrAlias=" ";
PlayParms.dwCallback=(DWORD)(this->m_hWnd); //将本对话框的句柄传给mci以接收消息
PlayParms.dwTo=0;
PlayParms.dwFrom=0;
mciSendCommand(NULL,MCI_OPEN,MCI_OPEN_TYPE|MCI_OPEN_ELEMENT,(DWORD)(LPVOID)&mciOpenParms);
mciSendCommand(mciOpenParms.wDeviceID,MCI_PLAY,MCI_NOTIFY,(DWORD)(LPVOID)&PlayParms);
//MCI_NOTIFY消息使得声音文件被异步播放,并在完成后向对话框发送一个MM_MCINOTIFY消息
m_wDeviceID=mciOpenParms.wDeviceID; //将设备ID保存到类的成员变量中
//改变按钮的属性
GetDlgItem(IDC_PLAY)->EnableWindow(FALSE);
GetDlgItem(IDC_PAUSE)->EnableWindow(TRUE);
GetDlgItem(IDC_STOP)->EnableWindow(TRUE);
GetDlgItem(IDC_DRAW)->EnableWindow(FALSE);
//得到文件长度,保存在类的成员变量中
MCI_STATUS_PARMS mciStatusParams;
mciStatusParams.dwItem=MCI_STATUS_LENGTH;
mciSendCommand(m_wDeviceID,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&mciStatusParams);
m_dwLength=mciStatusParams.dwReturn;
//将滚动条置零
m_slProgress.EnableWindow(TRUE);
m_slProgress.SetRange(0,100);
m_slProgress.SetPos(0);
m_dwPosition=0;
//开始以十分之一秒为间隔的计时器消息
SetTimer(IDT_PROGRESS,100,NULL);
SetTimer(IDT_FREQ,TIMESPICE,NULL);
m_bPlaying=TRUE;
}
//停止播放
void CWavePlayerDlg::OnStop()
{
//关闭mci设备
mciSendCommand(m_wDeviceID,MCI_CLOSE,NULL,NULL);
//使按钮恢复初态
GetDlgItem(IDC_PAUSE)->EnableWindow(FALSE);
GetDlgItem(IDC_STOP)->EnableWindow(FALSE);
GetDlgItem(IDC_PLAY)->EnableWindow(TRUE);
if (!m_bExtended)
GetDlgItem(IDC_DRAW)->EnableWindow(TRUE);
//停止计时器,恢复滚动条
KillTimer(IDT_PROGRESS);
KillTimer(IDT_FREQ);
m_slProgress.SetPos(0);
m_slProgress.EnableWindow(FALSE);
m_bPlaying=FALSE;
OnPaint();
}
//暂停、继续播放
void CWavePlayerDlg::OnPause()
{
static BOOL bPaused=FALSE;//静态局部变量,表示当前是否暂停
if (bPaused)
mciSendCommand(m_wDeviceID,MCI_RESUME,NULL,NULL);
else
mciSendCommand(m_wDeviceID,MCI_PAUSE,NULL,NULL);
bPaused=!bPaused;
// m_bPlaying=!m_bPlaying;
//更改按钮标题
GetDlgItem(IDC_PAUSE)->SetWindowText(bPaused ? "继续播放" : "暂停播放");
}
//关闭窗口
void CWavePlayerDlg::OnDestroy()
{
if (!m_sFile.IsEmpty())
OnStop(); //先停止播放
if (m_pBuffer!=NULL)
{
delete[] m_pBuffer;
m_pBuffer=NULL;
}
if (m_pFreq!=NULL)
{
delete[] m_pFreq;
m_pFreq=NULL;
}
CDialog::OnDestroy();
}
//计时器消息响应
void CWavePlayerDlg::OnTimer(UINT nIDEvent)
{
switch(nIDEvent)
{
case IDT_PROGRESS:
//得到当前播放位置
MCI_STATUS_PARMS mciStatusParams;
mciStatusParams.dwItem=MCI_STATUS_POSITION;
mciSendCommand(m_wDeviceID,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&mciStatusParams);
m_dwPosition=mciStatusParams.dwReturn;
//设定滚动条位置
m_slProgress.SetPos(m_dwPosition*100/m_dwLength);
break;
case IDT_FREQ:
if (m_bExtended)
{
DoFFT();
DrawEffect();
OnPaint();
}
break;
}
CDialog::OnTimer(nIDEvent);
}
//当播放完成时mci会向对话框发一个MM_MCINOTIFY消息,在此响应
void CWavePlayerDlg::OnComplete(WPARAM w,LPARAM l)
{
//当播放完后关闭设备,将一切恢复开始状态
OnStop();
}
void CWavePlayerDlg::OnDrawWave()//对用户按下“绘制波形”按钮的响应
{
int i,f;
CRect rc;
GetWindowRect(&rc);
//展开或收缩窗口
f=m_bExtended ? -1 : 1;
for (i=0;i<65;i+=1)
{
rc.bottom+=f*i/10;
MoveWindow(&rc,TRUE);
}
m_bExtended=!m_bExtended;
if (m_bExtended)
{
OnPaint();
GetDlgItem(IDC_DRAW)->EnableWindow(FALSE);//禁止用户再次按下这个按钮
}
}
//将波形从文件读入内存
void CWavePlayerDlg::ReadWave()
{
WORD wValue;
UINT nSize=m_WaveFileHead.datasize,nBytePerPoint=m_WaveFileHead.blockalign;
//长度和每个采样点的字节数
short nValue; //每个点的样值
BYTE bValue;
//以下打开文件并跳过文件头
CFile fWave;
fWave.Open(m_sFile,CFile::modeRead,NULL);
fWave.Seek(sizeof(WaveFileHead),0); //m_nExtra stands for the.......
if (m_pBuffer!=NULL) //若不是第一次读入数据,则释放掉刚才占有的内存
{
delete[] m_pBuffer;
m_pBuffer=NULL; //把指针设为空,似乎可以不要这一句,不过我觉得这么写是个好习惯
}
m_pBuffer=new short[nSize/nBytePerPoint]; //分配内存空间
m_nMaxValue=0;
for (UINT i=0;i myFFT((char *)m_pFreq,m_nFreqPoints);
m_nMaxValueFreq=myFFT.Do();
}
else
{
CFFT myFFT((short *)m_pFreq,m_nFreqPoints);
m_nMaxValueFreq=myFFT.Do();
}
}