当前位置:文档之家› 微型计算机接口技术大作业

微型计算机接口技术大作业

微型计算机接口技术大作业学院(系):软件学院专业:软件工程班级:姓名:学生学号:电话:2011年6月5日P2P语音聊天工具一、程序简介程序实现了文字聊天和语音聊天,程序即可以作为服务器端侦听其他用户的连接请求,也可以作为客户端主动连接其他用户。

程序使用了Win32 API提供的用于多媒体服务的函数接口,主要涉及波形音频设备的相关内容。

程序主要分为四个模块:1.主程序模块(消息处理和界面控制)2.负责接收连接的服务器Socket模块3.负责接收和发送数据的客户端模块4.声卡数据的采集和播放模块二、流程图主模块流程图:负责接收连接的服务器Socket模块:负责接收和发送数据的客户端模块:声卡数据的采集和播放模块流程图:三、代码实现代码主要是用MFC对话框程序框架编写的,对于界面上的控件和消息传递的部分,此处略去,仅仅给出主要程序的代码。

主模块代码://初始化端口号、聊天消息框和输入框CExample2_ChatRoomDlg::CExample2_ChatRoomDlg(CWnd* pParent /*=NULL*/) : CDialog(CExample2_ChatRoomDlg::IDD, pParent){//{{AFX_DATA_INIT(CExample2_ChatRoomDlg)m_sInputString = _T(""); //输入框m_sShowString = _T(""); //聊天消息框m_uPort = 4000; //端口号//}}AFX_DATA_INIT// Note that LoadIcon does not require a subsequent DestroyIcon in Win32m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);}//初始化服务器IP地址(默认设置为本机的IP地址)BOOL CExample2_ChatRoomDlg::OnInitDialog(){CDialog::OnInitDialog();ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != NULL){CStringstrAboutMenu;strAboutMenu.LoadString(IDS_ABOUTBOX);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}SetIcon(m_hIcon, TRUE);SetIcon(m_hIcon, FALSE);if(AfxSocketInit(NULL)==0){AfxMessageBox("CSocketInital Error");}BYTE f0,f1,f2,f3;CString name;CClientSocket::GetLocalHostName(name); //获取本机名CClientSocket::GetIpAddress(name,f0,f1,f2,f3); //获取本机IP//设置服务器IP为本机IP((CIPAddressCtrl *)(GetDlgItem(IDC_IPADDRESS)))->SetAddress(f0,f1,f2,f3);m_bInit=false;m_bClient=false;m_willchating=TRUE;return TRUE;}//显示主窗体BOOL CExample2_ChatRoomApp::InitInstance(){AfxEnableControlContainer();#ifdef _AFXDLLEnable3dControls();#elseEnable3dControlsStatic();#endifCExample2_ChatRoomDlg dlg; //创建窗体实例m_pMainWnd = &dlg;intnResponse = dlg.DoModal();if (nResponse == IDOK){}else if (nResponse == IDCANCEL){}return FALSE;}//对于用户更改聊天窗口内容的判断void CExample2_ChatRoomDlg::OnInputText(){if(!m_bInit) //如果用户还没有连接就输入聊天内容,提示用户先连接{AfxMessageBox("还没有连接到服务器!");return;}//如果用户按下Enter按钮,且焦点在输入窗口中,则对当前聊天室中的所//有用户发送消息CString in;CMessgmsg;GetDlgItemText(IDC_INPUTTEXT,in);if(in.GetLength()<1){return;} //如果用户没有输入内容,返回if(in.GetAt(in.GetLength()-1)=='\n'){in.TrimRight(" ");SetDlgItemText(IDC_INPUTTEXT,"");if(in.GetLength()>2){m_sMsgList+=in;SetDlgItemText(IDC_SHOWTEXT,m_sMsgList);Intm_iLineCurrentPos=((CEdit*)(GetDlgItem(IDC_SHOWTEXT)))->GetLineCount();((CEdit*)(GetDlgItem(IDC_SHOWTEXT)))->LineScroll(m_iLineCurrentPos);msg.m_strText=in;if(!m_bClient){POSITION pos;for(pos=m_connectionList.GetHeadPosition();pos!=NULL;){ //循环遍历所有建立连接的用户,分别发送消息CClientSocket * t= (CClientSocket *)m_connectionList.GetNext(pos);t->SendMessage(&msg);}}else{m_clientsocket.SendMessage(&msg);}}}}//建立服务器按钮void CExample2_ChatRoomDlg::OnSetserver(){if(!m_bInit){m_bClient=false; //作为服务器端m_bInit=true;if(m_pListenSocket.Init(GetDlgItemInt(IDC_PORT),this)==FALSE){ //初始化服务器(建立套接字并侦听,详见Server模块)m_bInit=false;return;}}}//连接服务器按钮void CExample2_ChatRoomDlg::OnConnectserver(){if(!m_bInit){BYTE f0,f1,f2,f3;CString name;//读取服务器IP输入框中的数据((CIPAddressCtrl *)(GetDlgItem(IDC_IPADDRESS)))->GetAddress(f0,f1,f2,f3);CStringip;ip.Format("%d.%d.%d.%d",f0,f1,f2,f3);m_bClient=true;m_clientsocket.Create();if(m_clientsocket.Connect(ip,GetDlgItemInt(IDC_PORT))) //连接服务器{m_clientsocket.Init(this);SetDlgItemText(IDC_SHOWTEXT,"成功连接到服务器.");m_bInit=true;}else{m_clientsocket.Close();AfxMessageBox("连接服务器失败!");m_bInit=false;}}}//用户按下语音聊天按钮void CExample2_ChatRoomDlg::OnNewsend(){if(m_willchating==TRUE){m_sound.Init(this); //初始化音频m_sound.Record(); //开始录音SetDlgItemText(IDC_NEWSEND,"停止语音聊天");m_willchating=FALSE;}else{CSingleLocklock(&m_mutex,TRUE);m_sound.StopRecord(); //停止录音SetDlgItemText(IDC_NEWSEND,"语音聊天");m_willchating=TRUE;lock.Unlock();}}负责接收连接的服务器Socket模块代码://服务器初始化BOOL CServerSocket::Init(UINT port, CExample2_ChatRoomDlg* dlg){m_uPort=port;m_dlg=dlg;if(Create(m_uPort)==FALSE) //创建套接字{AfxMessageBox("Server Socket Create Error");return FALSE;}if(this->Listen()==FALSE) //侦听{AfxMessageBox("Server Listen Error");return FALSE;}m_dlg->SetDlgItemText(IDC_SHOWTEXT,"正在侦听其他用户的连接请求!");return TRUE;}//当有连接请求时把该用户加入连接队列中void CExample2_ChatRoomDlg::ProcessPendingAccept(){CClientSocket* pSocket = new CClientSocket(); //为该用户分配一个套接口if (m_pListenSocket.Accept(*pSocket)) //接收连接请求{CMessgmsg;msg.m_strText="一个游客进入聊天室了\n";m_sShowString+="一个游客进入聊天室了\n";POSITION pos;//向所有用户发送消息for(pos=m_connectionList.GetHeadPosition();pos!=NULL;){CClientSocket * t= (CClientSocket *)m_connectionList.GetNext(pos);t->SendMessage(&msg);}pSocket->Init(this);m_connectionList.AddTail(pSocket);}elsedeletepSocket;}负责接收和发送数据的客户端模块代码://发送消息BOOL CClientSocket::SendMessage(CMessg * msg){if (m_aSessionOut != NULL){msg->Serialize(*m_aSessionOut);m_aSessionOut->Flush();return TRUE;}else{//对方关闭了连接m_bClose=true;CloseSocket();m_dlg->CloseSessionSocket();return FALSE;}}//接收消息voidCClientSocket::OnReceive(intnErrorCode){CSocket::OnReceive(nErrorCode);do{CMessg temp;temp.Serialize(*m_aSessionIn);m_dlg->m_sMsgList+=temp.m_strText; //添加条目m_dlg->SetDlgItemText(IDC_SHOWTEXT,m_dlg->m_sMsgList);if(temp.m_tag==1&&m_dlg->m_willchating==FALSE)//如果有声音过来并且本机的声音设备已经准备好了则首先在本机发出声音{memcpy(m_dlg->m_sound.m_cBufferOut,temp.m_buffer,MAX_BUFFER_SIZE);}intlinenum=((CEdit*)(m_dlg->GetDlgItem(IDC_SHOWTEXT)))->GetLineCount();((CEdit *)(m_dlg->GetDlgItem(IDC_SHOWTEXT)))->LineScroll(linenum);if(!m_dlg->m_bClient) //如果是服务器端的话,转发消息{for(POSITIONpos=m_dlg->m_connectionList.GetHeadPosition();pos!=NULL;){CClientSocket * t = (CClientSocket*)m_dlg->m_connectionList.GetNext(pos);if(t->m_hSocket!=this->m_hSocket){t->SendMessage(&temp);}}}}while (!m_aSessionIn->IsBufferEmpty());}声卡数据的采集和播放模块代码://初始化voidCSound::Init(CExample2_ChatRoomDlg *dlg){int result;m_dlg=dlg;if(waveInGetNumDevs()==0) //是否有音频输入设备{AfxMessageBox("There is no sound input device");}if(waveOutGetNumDevs()==0) //是否有音频输出设备{AfxMessageBox("There is no sound output device");}//指定录音格式m_soundFormat.wFormatTag=WAVE_FORMAT_PCM;m_soundFormat.nChannels=1;m_soundFormat.nSamplesPerSec=8000;m_soundFormat.nAvgBytesPerSec=16000;m_soundFormat.nBlockAlign=2;m_soundFormat.cbSize=0;m_soundFormat.wBitsPerSample=16;int res=waveInOpen(&m_hWaveIn,WAVE_MAPPER, &m_soundFormat, (DWORD)m_dlg->m_hWnd,0L,CALLBACK_WINDOW); //打开录音设备if(res!= MMSYSERR_NOERROR){AfxMessageBox("Error in waveInOpen function");return ;}res=waveOutOpen(&m_hWaveOut,WAVE_MAPPER, &m_soundFormat, (DWORD)m_dlg->m_hWnd,0L,CALLBACK_WINDOW); //打开录音设备if(res!= MMSYSERR_NOERROR){AfxMessageBox("Error in waveOutOpen function");return ;}//inm_pWaveHdrIn[0].lpData=m_cBufferIn;m_pWaveHdrIn[0].dwBufferLength=MAX_BUFFER_SIZE;m_pWaveHdrIn[0].dwBytesRecorded=0;m_pWaveHdrIn[0].dwFlags=0;result=waveInPrepareHeader(m_hWaveIn,&m_pWaveHdrIn[0],sizeof(WAVEHDR) ); //准备内存块录音if (result!= MMSYSERR_NOERROR){AfxMessageBox(_T("Cannot InPrepareHeader !"));return;};//增加内存块result=waveInAddBuffer(m_hWaveIn,&m_pWaveHdrIn[0],sizeof(WAVEHDR));if (result!= MMSYSERR_NOERROR){AfxMessageBox(_T("Cannot InAddBuffer !"));return;};//outm_pWaveHdrOut[0].lpData=m_cBufferOut;m_pWaveHdrOut[0].dwBufferLength=MAX_BUFFER_SIZE;m_pWaveHdrOut[0].dwBytesRecorded=0;m_pWaveHdrOut[0].dwFlags=0;waveOutPrepareHeader(m_hWaveOut,&m_pWaveHdrOut[0],sizeof(WAVEHDR)); //准备内存块录音waveOutWrite(m_hWaveOut,&m_pWaveHdrOut[0],sizeof(WAVEHDR));}//开始录音voidCSound::Record(){waveInStart(m_hWaveIn);//开始录音}//播放本地声音voidCSound::Play(){memcpy(m_cBufferOut,m_cBufferIn,MAX_BUFFER_SIZE);}//停止录音voidCSound::StopRecord(){waveInStop(m_hWaveIn); //停止录音waveInReset(m_hWaveIn); //清空内存块}//清空录音缓冲区voidCSound::FreeRecordBuffer(){intresult=waveInUnprepareHeader(m_hWaveIn,&m_pWaveHdrIn[0],sizeof(WAVEHDR)); if (result!= MMSYSERR_NOERROR){AfxMessageBox(_T("Cannot UnInPrepareHeader !"));return;}; //inm_pWaveHdrIn[0].lpData=m_cBufferIn;m_pWaveHdrIn[0].dwBufferLength=MAX_BUFFER_SIZE;m_pWaveHdrIn[0].dwBytesRecorded=0;m_pWaveHdrIn[0].dwFlags=0;result=waveInPrepareHeader(m_hWaveIn,&m_pWaveHdrIn[0],sizeof(WAVEHDR) ); //准备内存块录音if (result!= MMSYSERR_NOERROR){AfxMessageBox(_T("Cannot InPrepareHeader !"));return;}; //inresult=waveInAddBuffer(m_hWaveIn,&m_pWaveHdrIn[0],sizeof(WAVEHDR)); //增加内存块if (result!= MMSYSERR_NOERROR){AfxMessageBox(_T("Cannot InAddBuffer !"));return;}; //in}//清空输出缓冲区voidCSound::FreePlayBuffer(){staticbool once=true;int result;result=waveOutUnprepareHeader(m_hWaveOut,&m_pWaveHdrOut[0],sizeof(W AVEHDR));m_pWaveHdrOut[0].lpData=m_cBufferOut;m_pWaveHdrOut[0].dwBufferLength=MAX_BUFFER_SIZE;m_pWaveHdrOut[0].dwBytesRecorded=0;m_pWaveHdrOut[0].dwFlags=0;result=waveOutPrepareHeader(m_hWaveOut,&m_pWaveHdrOut[0],sizeof(WAV EHDR)); //准备内存块录音if (result!= MMSYSERR_NOERROR){AfxMessageBox(_T("Cannot OutPrepareHeader !"));return;}; //outresult=waveOutWrite(m_hWaveOut,&m_pWaveHdrOut[0],sizeof(WAVEHDR)); //增加内存块if (result!= MMSYSERR_NOERROR){AfxMessageBox(_T("Cannot OutWrite !"));return;}; //out}//输入缓冲区满的时候触发的发送音频消息函数void CExample2_ChatRoomDlg::WriteBufferFull(LPARAM lp,WPARAMwp){m_sound.Play();//发出本地声音CSingleLocklock(&m_mutex,TRUE);CMessgmsg;msg.m_strText="";msg.m_tag=1;memcpy(msg.m_buffer,m_sound.m_cBufferIn,MAX_BUFFER_SIZE);if(!m_bClient){POSITION pos;for(pos=m_connectionList.GetHeadPosition();pos!=NULL;){ //如果是服务器端,就给所有客户端发送音频CClientSocket * t= (CClientSocket *)m_connectionList.GetNext(pos);t->SendMessage(&msg);}}else{m_clientsocket.SendMessage(&msg);}m_sound.FreeRecordBuffer();m_sound.FreePlayBuffer();lock.Unlock();}四、程序运行结果截图五、心得与体会通过学习大作业和平时的小作业,了解并掌握了网络通信编程、视频和音频传输技术,对于MPI、语音识别技术也有了一定的了解。

相关主题