WIRELESS INSTANT VOICE MESSAGING USING THE INTERNET By CARLOS A. RIVERA-CINTRON A THESIS PRESENTED TO THE GRADUATE SCHOOL OF THE UNIVERSITY OF FLORIDA IN PARTIAL FULFILLMENT OF THE REQUIREMENTS FOR THE DEGREE OF MASTER OF SCIENCE UNIVERSITY OF FLORIDA 2000
270
Embed
WIRELESS INSTANT VOICE MESSAGING USING THE INTERNETufdcimages.uflib.ufl.edu/UF/00/10/07/18/00001/thesisBook_4_26.pdfwill present a historical background into voice messaging applications
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
WIRELESS INSTANT VOICE MESSAGING USING THE INTERNET
By
CARLOS A. RIVERA-CINTRON
A THESIS PRESENTED TO THE GRADUATE SCHOOLOF THE UNIVERSITY OF FLORIDA IN PARTIAL FULFILLMENT
OF THE REQUIREMENTS FOR THE DEGREE OFMASTER OF SCIENCE
UNIVERSITY OF FLORIDA
2000
I dedicate this work to my loving wife, Odemaris, our daughter Adriana Maria,
our soon to be born child, and my relatives in Puerto Rico. Thanks for your patience,
and understanding (Los Amo!).
iii
ACKNOWLEDGMENTS
Above all, I thank God for providing me the resources, health, courage and
strength necessary to start and complete this work.
Also, my deepest gratitude goes to my managers and colleagues at Motorola for
1.2.1 History of Paging............................................................................................ 11.2.2 The Messaging Device ................................................................................... 3
1.3 Voice Messaging Using The Internet .................................................................... 51.4 Wireless Voice Messaging – Thesis Motivation................................................... 61.5 Wireless Voice Messaging Components............................................................... 71.6 System Requirements .......................................................................................... 10
2 TRANSMITTING VOICE OVER AN INTERNET BASED NETWORK.............. 13
2.1 Voice Into The Computer.................................................................................... 132.2 Classes Of Codecs............................................................................................... 14
2.3 Codec Intelligibility Vs Naturalness ................................................................... 172.4 Voice Over IP Version 6 ..................................................................................... 19
2.4.1 Mobility support - AutoConfiguration ......................................................... 202.4.2 Quality of Service (QoS) - Multimedia features .......................................... 212.4.3 IPv6 Header.................................................................................................. 21
2.5 H.323................................................................................................................... 232.6 Quality Of Service Issues For Voice Over Packet Networks .............................. 25
2.6.1 Delay ............................................................................................................ 252.6.2 Jitter.............................................................................................................. 262.6.3 Lost Packets.................................................................................................. 27
4 WIRELINE TO WIRELESS INTERNET GATEWAY ........................................... 50
4.1 Introduction ......................................................................................................... 504.2 Mobile IP............................................................................................................. 514.3 iDEN Overview and Architecture ....................................................................... 544.4 GPRS Overview and Architecture ...................................................................... 564.5 Summary ............................................................................................................. 57
5 WIRELESS INTERNET VOICE MESSENGER ..................................................... 59
5.1 Introduction ......................................................................................................... 595.2 Wireless Voice Messenger Server – The OS ...................................................... 605.3 Wireless Voice Messenger Server – The Application Software ......................... 615.4 Wireless IP Network Interface - RF Modem....................................................... 68
5.4.1 PPP and IP address publication via a Mobile IP capable RF modem .......... 735.4.2 PPP and IP address publication via a GPRS capable RF modem ................ 75
7.2.1 Requirements................................................................................................ 967.2.2 Implementation Details for Server ............................................................... 96
7.3 Alternative to Real Time IP Telephony............................................................... 977.4 High Compression Codec.................................................................................... 98
In this work we have presented a model of wireless servers, which is a different
paradigm than the traditional client (mobile)/server (static) approach. Our intention was
to demonstrate one use for such model, which is enabled by the application of Mobile IP
to packet data networks, GPRS, and user name lookup techniques (like ICQ). The
computing power required by these wireless messaging servers can and will be exploited
by the mobile phone/laptop computer combination approach. The number of fast and full
applications used in laptops, and the fast air interfaces promised by the CDMA HDR,
GPRS EDGE, etc; will permit users to keep this same usage model until PDAs begin to
replace laptops.
102
APPENDIX ASCREEN CAPTURES OF VMC AND VMS
Figure A.1 Default Instant Voice Message Client When ICQ is Not Running
103
Figure A.2 Default Instant Voice Message Client GUI When ICQ Is Running
104
Figure A.3 Instant Voice Message Client GUI After Entering Fields, And “Push To Talk”Is Clicked
105
Figure A.4 Instant Voice Message Client GUI After Clicking On “Send Pre-RecordedMessage”
106
Figure A.5 Instant Voice Message Client GUI After Message Has Been Successfully Sent
107
Figure A.6 Instant Voice Message Server Console Waiting For A Connecting Client
108
Figure A.7 Instant Voice Message Server Console Receiving A Voice File From A Client
109
Figure A.8 Instant Voice Message Server Console After A Voice File Has Been Received
110
Figure A.9 Instant Voice Message Server Console While A Voice File Is Being Playback
111
APPENDIX BVOICE MESSAGING CLIENT AND VOICE MESSAGING SERVER SOURCE CODE
B.1 Voice Messaging Client Source Code
B.1.1 Gui Dll
<! IEGUI.htm = This is the html file used to load the GUI inside INternet Explorer ><HTML><HEAD><TITLE>ATL 3.0 test page for object IEGui</TITLE></HEAD><BODY><OBJECT ID="IEGui" CLASSID="CLSID:19A2966E-5122-11D3-BD2C-828A6439BC24"></OBJECT></BODY></HTML>
//*****************************************************// IEGui.h : Declaration of the CIEGui//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Gui.dll workspace. Changes made by the author to the// file are marked by 'Comment by CR:'.// This is the header file implementation of the CIEGui class.// CIEGui is the C++ dependent implementation of the IEGui coclass.//-----------------------------------------------------
#ifndef __IEGUI_H_#define __IEGUI_H_
#include "resource.h" // main symbols#include <atlctl.h>
// Comment by CR: This is the header file for the client dialog box definition#include "stringdlg.h"
/////////////////////////////////////////////////////////////////////////////// CIEGui// Comment by CR: CIEGui definition with all of the Internet Explorer required interfacesclass ATL_NO_VTABLE CIEGui :
public CComObjectRootEx<CComSingleThreadModel>,public IDispatchImpl<IRecordingListener, &IID_IRecordingListener, &LIBID_GUILib>,public CComControl<CIEGui>,public IPersistStreamInitImpl<CIEGui>,
HRESULT InPlaceActivate( LONG iVerb, const RECT* prcPosRect);
private:RECT m_rc;
// Comment by CR: This is the Dialog class used by the CIEGui to implement all the necessary dialog// controls (buttons, edit boxes, etc.)CStringDlg m_CVmcDialog;
};
#endif //__IEGUI_H_
//*****************************************************// stdafx.h : include file for standard system include files,// or project specific include files that are used frequently,// but are changed infrequently//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Gui.dll workspace. No changes were made by the author.//-----------------------------------------------------
#include <atlbase.h>//You may derive a class from CComModule and use it if you want to override//something, but do not change the name of _Moduleextern CComModule _Module;#include <atlcom.h>#include <atlctl.h>
//{{AFX_INSERT_LOCATION}}// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
//*****************************************************// stringdlg.h//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file is the main voice messaging client window.// It implements a dialog box class with controls to record// cancel, and send messages. Provides input to the user// for the IP address or ICQ number of the destination// machine. The ICQ api is used to convert an ICQ number to// an IP address.//-----------------------------------------------------
// Comment by CR: This include provides COM definitions for the controls inside// the dialog. This software DOES NOT use MFC, so AtlControls.h is the// answer to use buttons, combo boxes, etc. in a dialog#include <AtlControls.h>#include <math.h>#include <time.h>
// Comments by CR: These are the ICQ APIs defines. These defines are// implemented in the#include "ICQAPINotifications.h"#include "ICQAPICalls.h"#include "ICQAPIData.h"#include "ICQAPIInterface.h"
#pragma once#include "resource.h"
// Comment by CR: Record.tlb is the type library that contains the Recorder// component and the ISpeechRecorder interface.#import "Record.tlb" raw_interfaces_only, no_namespace, named_guids#define lengthof(rg) (sizeof(rg)/sizeof(*rg))
#define IDLE 0#define RECORDING 1
void __stdcall OnReady();
class CStringDlg; //forward declaration, allows the next line to compile properly// Comment by CR: typedef for the IDispEventImpl<> ATL template to support connection points on// this class event sink maptypedef IDispEventImpl<0, CStringDlg, &DIID__IRecorderEvents, &LIBID_RECORDLib, 1, 0> IEventSink;
// Comment by CR: CStringDlg class which implements an instance of an ATL CDialogImpl// template class. This is the dialog box that implements the IDD_DIALOG1 dialog// resource of Gui. Also implements the behavior of an event sink to receive// 'Done recording' messages from the Recorder component.class CStringDlg : public CDialogImpl<CStringDlg>,
public IEventSink //CR.{public:
// Comment by CR: Constructor initializes all member attributes CStringDlg() {
*m_sz = 0;m_nStatus=IDLE;m_lpszIPAddress = new char [16];memset(m_lpszIPAddress,16,NULL);m_nIcqApiVersion=-1;
115
}
// Comment by CR: The sink map defines the interface that fires the event on the source (Recorder)// and the method (OnReady) to be called from this class (sink)BEGIN_SINK_MAP(CStringDlg)
// Comment by CR: Message map defines method calls when a message is handled by WindowsBEGIN_MSG_MAP(CStringDlg) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_ID_HANDLER(IDOK, OnOK) // Comment by CR: When clicking Close button COMMAND_ID_HANDLER(IDCANCEL, OnCancel) // Comment by CR: When clicking Stop Talking COMMAND_ID_HANDLER(IDC_PTT, OnPtt) // Comment by CR: When clicking Push to Talk COMMAND_ID_HANDLER(IDC_ABORT, OnAbort) // Comment by CR: When clicking Abort COMMAND_ID_HANDLER(IDC_SEND_PRERECORDED, OnSendPrerecorded) // Comment by CR: When clicking Send pre-recordedEND_MSG_MAP()
enum { IDD = IDD_DIALOG1 }; TCHAR m_sz[64];
// Comment by CR: This is the event sink method is called by the event source (Recorder) // when a certain event is completed in the source. The events handled are the start // and stop talking events, to allow for the voice message to be transfered properly. void __stdcall OnReady() {
private: // Comment by CR: when first time, Gui initializes all smart pointers. If not first time // it will use these smart pointers, as they have been init already. This permits the Gui // to be hidden minimized without having to recreate the poiters to the COM server. BOOL m_bFirstTime; // Comment by CR: These are the smart pointers used to access the interfaces in the // Record DLL. CComQIPtr<ISpeechRecorder> m_spSR; CComQIPtr<ISpeechMessenger> m_spSM;
// Comment by CR: Method called when the Gui is loaded in the Internet Explorer LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) {
// Comment by CR: Initialize the ICQ library. Enter here a new name, password and IDICQAPICall_SetLicenseKey("LicenseeName","LicenseePassword","LicenseNumber");// Comment by CR: If the password and License key worked okICQAPICall_GetVersion(m_nIcqApiVersion);
CenterWindow();
// Comment by CR: Assign the IDD_DIALOG1 controls to the IEGui dialog m_Edit.Attach(GetDlgItem(IDC_EDIT1));
LRESULT hr=0;// Comment by CR: If this is the 1st the Gui is loaded initialize the connection// to the Recorder ISpeechRecorder interface by creating/init the smart pointerif ( m_bFirstTime ){
m_bFirstTime = FALSE;
// Comment by CR: Get the ISpeechRecorder from the Record.dllhr = (LRESULT) m_spSR.CoCreateInstance (__uuidof(Recorder));// Comment by CR: Once the pointer is initialized and pointing the created// instance to the Recoder component, it can be used to query other interfaces,// in this case the ISpeechMessenger.m_spSR->QueryInterface(reinterpret_cast<ISpeechMessenger **> (&m_spSM));
if (SUCCEEDED(hr)){
// Comment by CR: Check that the source has been initialized. Init is done by// the constructor of the _IDispEvent class and means that the connection point// can be connected and disconnected from the sinkif ( IEventSink::m_dwEventCookie != 0xFEFEFEFE )
DisconnectServer();
try{
// Comment by CR: Check the IRecorderEvents interface (event source)// interface is validhr = IEventSink::DispEventAdvise( m_spSR, & DIID__IRecorderEvents);if (FAILED(hr))
if ( m_spSM->SetWavFileName(szWavFileName) == S_OK ) this->SendMsg();
else {
m_Edit.SetWindowText("Wav file was not found or could not be opened!"); return ( ! S_OK );
} } else
m_Edit.SetWindowText("No Wav file name was specified ! Try again.");
return S_OK; } else
return ( ! S_OK ); } else {
m_Edit.SetWindowText("PTT was pressed first! Abort or Stop Talking before attempting to send file"); return ( ! S_OK );
} }
// Comment by CR: When clicking Push to Talk, Windows calls this method LRESULT OnPtt(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) //CR. { if ( this->FetchIPAddress() == TRUE )
{ m_Edit.SetWindowText("Wait for signal to start talking ...");
// Comment by CR: Connect to the StartRecording method of the ISpeechRecorder interface // When is completed, StartRecording fires an event which the the CStringDlg::OnReady() // method if ( m_spSR->StartRecording() != S_OK ) {
m_Edit.SetWindowText("Error in Voice Capture hardware. Close application!"); return ( ! S_OK );
}
// Comment by CR: Update the status attribute so that the Gui knows what the recorder is // doing m_nStatus = RECORDING; return S_OK;
} else
return ( ! S_OK ); }
// Comment by CR: Called when user click on Stop Talking LRESULT OnCancel(WORD, UINT, HWND, BOOL&) { // Comment by CR: Check if recording. Can only stop if recording not if idle.
if ( m_nStatus == RECORDING ){
m_Edit.SetWindowText("Ready to stop recording.");
// Comment by CR: Stop recording and let the message be sent to the userif ( m_spSR->StopRecording() != S_OK ){
m_Edit.SetWindowText("Error in Voice Capture hardware. Close application!");
118
return 0;}
// Comment by CR: Using the ISpeechMessenger interface, send the message to the// final IP addressthis->SendMsg();// Comment by CR: System is now ready to record againm_nStatus = IDLE;
}else
m_Edit.SetWindowText("No message. Press Push To Talk first.");
return 0; }
// Comment by CR: When clickin Close, release the handles to the interfaces in Record.dll // so that OS can free up the DLL. Also close the Window, which will require reload to get // Gui to start again LRESULT OnOK(WORD, UINT, HWND, BOOL&) {
m_spSR.Release();m_spSM.Release();
CWindow::DestroyWindow(); return 0; }
// Comment by CR: When clickin Abort, if user does not want to send message LRESULT OnAbort(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) //CR. {
if ( m_nStatus == RECORDING ){
m_Edit.SetWindowText("Ready to Abort recording.");// Comment by CR: StopRecording and set the state to IDLEif ( m_spSR->StopRecording() != S_OK ){
m_Edit.SetWindowText("Error in Voice Capture hardware. Close application!");return 0;
}m_nStatus = IDLE;m_Edit.SetWindowText("Ready to record.");
}else
m_Edit.SetWindowText("Not recording. Press Push To Talk first.");
return 0; }
private:
// Comment by CR: These are the private attributes of the class int m_nStatus; ATLControls::CIPAddressCtrl m_IP; ATLControls::CEdit m_Edit; ATLControls::CComboBox m_IcqNumberCombo; ATLControls::CComboBox m_MtuSize; ATLControls::CComboBox m_PrerecordedFileName; LPTSTR m_lpszIPAddress; UINT m_SocketPort; int m_nIcqApiVersion;
// Comment by CR: This method is used to disconnect from the connection point // event source (IRecorderEvents interface) void DisconnectServer() {
// Comment by CR: Initialize the array and ICQ user struct memset(szIcqNumber,NULL,sizeof(szIcqNumber)); memset(&bsiIcqUser,0,sizeof(bsiIcqUser));
// Comment by CR: Accept ICQ number only if ICQ is running if ( m_nIcqApiVersion != -1 )
m_IcqNumberCombo.GetWindowText(szIcqNumber,255);
// Comment by CR: ICQ number has precedence, so check if it is there if ( !strlen(szIcqNumber) && m_IP.IsBlank() == TRUE ) {
// Comment by CR: At least an IP or ICQ number needs to be present if ( m_nIcqApiVersion != -1 )
m_Edit.SetWindowText("Please enter valid ICQ number OR IP address first."); else
m_Edit.SetWindowText("Please enter valid IP address first (ICQ is not running)."); return FALSE;
} else if ( strlen(szIcqNumber) )
120
{ // Comment by CR: ICQ number found, clear the IP address field and store the // ICQ number in the ICQ data structure m_IP.ClearAddress(); sscanf(szIcqNumber,"%d", &bsiIcqUser.m_iUIN);
// Comment by CR: Call the ICQ API to get the IP address of the ICQ number ICQAPICall_GetFullUserData(& bsiIcqUser,m_nIcqApiVersion); // Comment by CR: IP address returns in nondot format, convert it to dot format; which // is most significant first DWORD field0, field1, field2, field3; field0 = 0; field1 = 0; field2 = 0; field3 = 0;
(field3=((bsiIcqUser.m_iIP)&0xFF000000)/0x00FFFFFF)); // Comment by CR: Print the found IP address into the IP control. First need to convert // it most significant last. ICQ returns the IP address backwards from the way our IP control // requires it. dwAddress = (field0*0x01000000) +
// Comment by CR: Release the ICQ structure. This is done so that if user details change // ( user goes offline back online), the Gui grabs the latest information. ICQAPIUtil_FreeUser( & bsiIcqUser ); return TRUE;
} else // Comment by CR: Fetch the IP address from the control not ICQ {
// Comment by CR: An ICQ number is not found, then use the IP address entered m_IP.GetAddress(&dwAddress); // Comment by CR: Conver the number to dot format, which is least significant first sprintf(m_lpszIPAddress,"%u.%u.%u.%u",(dwAddress&0xFF000000)/0x00FFFFFF,
(dwAddress&0x00FF0000)/0x0000FFFF,
(dwAddress&0x0000FF00)/0x000000FF,
dwAddress&0x000000FF); return TRUE;
} }};
//******************************************************// Gui.cpp : Implementation of DLL Exports.// Note: Proxy/Stub Information// To build a separate proxy/stub DLL,// run nmake -f Guips.mk in the project directory.//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000
121
//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Gui.dll workspace. No changes were made to the// automatically generated version.// Gui.dll is the COM server DLL that provides the interfaces// required to be loaded into an Internet Explorer control.//-----------------------------------------------------
//*****************************************************// Gui.idl : IDL source for Gui.dll//
// This file will be processed by the MIDL tool to// produce the type library (Gui.tlb) and marshalling code.//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Gui.dll workspace. Changes made by the author to the// file are marked by 'Carlos:'.// Gui.dll is the COM server DLL that provides the interfaces// required to be loaded into an Internet Explorer control.// This file provides the interface definition language (idl)// for the interfaces exposed by coclasses in the Gui.dll.//-----------------------------------------------------
// IRecordingListener Interface// Comments by CR: The IRecordingListener dispatch interface is to be implemented by the// DispatchImpl class of the IEGui coclass. Internet Explorer compatible components// (like IEGui) require a Dispatch interface. No methods are implemented because this// interface is being provided for compatibility with IE.[
[uuid(19A29661-5122-11D3-BD2C-828A6439BC24),version(1.0),helpstring("Gui 1.0 Type Library")
]library GUILib{
importlib("stdole32.tlb");importlib("stdole2.tlb");// Carlos: Because the Gui is a COM client of the Recorder component// defined in the Record.dll, include the type library to allow smart// pointers to access the interfaces in the Recorder compoenent.importlib("..\Record\Record.tlb");
// Carlos: The IEGui coclass is the actual component of the Gui.dll that implements// an Internet Explorer compatible control.
[default] interface IRecordingListener;// Carlos: This is the connection point sink (connection point receiver)// to the Recorder component connection point source (connection point trigger).dispinterface _IRecorderEvents;
};};
//*****************************************************// IEGui.cpp : Implementation of CIEGui//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Gui.dll workspace. Changes made by the author to the// file are marked by 'Carlos:'.// This is the implementation of the CIEGui class. CIEGui// the C++ dependent implementation of the IEGui coclass.//-----------------------------------------------------#include "stdafx.h"#include "Gui.h"#include "IEGui.h"
/////////////////////////////////////////////////////////////////////////////// CIEGui// Comment by CR: This method is called when the component is being loaded in the// parent window. It calls the base class InPlaceActivate() then prepares the// Voice Messaging Client (CVmcDialog) to be presented in the parent window// (Internet Explorer)HRESULT CIEGui::InPlaceActivate( LONG iVerb, const RECT* prcPosRect){
// Comment by CR: create the m_CVmcDialog window, then show it inside the parentm_CVmcDialog.Create( ::GetActiveWindow(), m_rc, NULL );m_CVmcDialog.EnableWindow(TRUE);m_CVmcDialog.ShowWindow(TRUE);
return hr;}
//*****************************************************// stdafx.cpp : source file that includes just the standard includes// stdafx.pch will be the pre-compiled header// stdafx.obj will contain the pre-compiled type information//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron
124
// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Gui.dll workspace. No changes were made by the author.// Gui.dll is the COM server DLL that provides the interfaces// required to be loaded into an Internet Explorer control.//-----------------------------------------------------#include "stdafx.h"
//*****************************************************// AcmApp.h = Includes all the necessary ovrhead for the// Audio Control Manager (ACM) and the Media Control Interface// (MCI)//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was adapted from the AcmApp application in the// Microsoft Developer's network library.//-----------------------------------------------------
//*****************************************************// RecordCP.h : RecorderEvents "fire" methods.//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the IRecorderEvents connection points. No changes were made by// the author. THis is the code that calls the event sink when// events happen.//-----------------------------------------------------#ifndef _RECORDCP_H_#define _RECORDCP_H_
128
template <class T>class CProxy_IRecorderEvents : public IConnectionPointImpl<T, &DIID__IRecorderEvents, CComDynamicUnkArray>{
//Warning this class may be recreated by the wizard.public:
//*****************************************************// stdafx.h : include file for standard system include files,// or project specific include files that are used frequently,// but are changed infrequently//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//
129
// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Record.dll workspace. No changes were made by the author.//-----------------------------------------------------#if !defined(AFX_STDAFX_H__6BFBE224_4F94_11D3_BD2C_B7B20D959A24__INCLUDED_)#define AFX_STDAFX_H__6BFBE224_4F94_11D3_BD2C_B7B20D959A24__INCLUDED_
#include <atlbase.h>//You may derive a class from CComModule and use it if you want to override//something, but do not change the name of _Moduleextern CComModule _Module;#include <atlcom.h>
//{{AFX_INSERT_LOCATION}}// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
//*****************************************************//{{NO_DEPENDENCIES}}// Microsoft Developer Studio generated include file.// Used by Record.rc////-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Record.dll workspace.//-----------------------------------------------------#define IDS_PROJNAME 100#define IDR_RECORDER 101
// Next default values for new objects//#ifdef APSTUDIO_INVOKED#ifndef APSTUDIO_READONLY_SYMBOLS#define _APS_NEXT_RESOURCE_VALUE 201#define _APS_NEXT_COMMAND_VALUE 32768#define _APS_NEXT_CONTROL_VALUE 201#define _APS_NEXT_SYMED_VALUE 102#endif#endif
//*****************************************************// Record.cpp : Implementation of DLL Exports.
130
//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Record.dll workspace. No changes were made to the// automatically generated version.// Record.dll is the COM server DLL that provides the interfaces// required to record a voice/sound by using the Microsoft Audio// Control manager.//-----------------------------------------------------
// Note: Proxy/Stub Information// To build a separate proxy/stub DLL,// run nmake -f Recordps.mk in the project directory.
//*****************************************************// Record.idl : IDL source for Record.dll//
// This file will be processed by the MIDL tool to// produce the type library (Record.tlb) and marshalling code.//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Record.dll workspace. Changes made by the author to the// file are marked by 'Carlos:'.// Record.dll is the COM server DLL that provides the interfaces// required to record a voice/sound.// This file provides the interface definition language (idl)// for the interfaces exposed by coclasses in the Record.dll.//-----------------------------------------------------
import "oaidl.idl";import "ocidl.idl";
// Comments by CR: The ISpeechRecorder provides the methods required to// record the voice/sound message[
// Comments by CR: The ISpeechCompressor is required to compress and uncompress the// after it has been captured. This interface is not currently implemented, but its// methods are stubbed and should be called from the COM client to permit future// compatibility.[
// Comments by CR: The ISpeechMessenger interface implements the functions// require to package the voice/sound message and send it to an IP network// aware component[
// Comments by CR: The IRecorder dispatch interface is to be implemented by the// DispatchImpl class of the CRecorder coclass. No methods are implemented because this// interface is being provided for compatibility with automation objects (such as VB).[
//*****************************************************// Recorder.cpp : Implementation of CRecorder//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Record.dll workspace. Changes made by the author to the// file are marked by 'Comments by CR:'.// This is the implementation of the CRecorder class. CRecorder// the C++ dependent implementation of the Recorder coclass.//// Recorder is a COM component that permits the user to record a// PCM WAV file, compress it, and send it to a TCP/IP enabled COM// component (Talker) so that it can be sent to a listenning socket// server.//// REUSE NOTE: This file contains code adapted and modified by the// author, from Microsoft's AcmApp application found in the// Developer's Network Library.//-----------------------------------------------------#include "stdafx.h"#include "Record.h"#include "Recorder.h"
// Comments by CR: This define is used to conditionally compile the section of// code that permits introducing a high compression codec into the software.//#define DEBUGGING_COMPRESSION 0
// Comment by CR: OPen and close the file to check for its existence. OPenFile()// with the OF_EXIST parameter will open and close the file immediately.
//******************************************************************************// Comments by CR: THe send method receives the IP address of the destination// IP computer, and calls the Talk method of the ITalker interface to route// the message//-----------------------------------------------------------------------------STDMETHODIMP CRecorder::Send(LPSTR pAddress, UINT nPacketSize){
// Comments by CR: set the packet size for the transferm_spTalkerClient->SetPacketSize(nPacketSize);// Comments by CR: set the address to the predetermined listening port// on the serverm_spTalkerClient->SetListenerAddress(pAddress,VOICE_MESSENGER_PORT_NUM);char szVoiceFilePath[256];
// Comments by CR: Check if the file to transfer is different than the default,// then send that one. m_szWavFileName is initialized to NULLif ( strlen(m_szWavFileName) )
// Comments by CR: if required, compress the speech to save bandwidth when transmitting.// CompressSpeech() will expect to compress a PCM wav file named VOICE_FILE_PATH/VOICE_FILE_NAME,// and save it under the same name// Note: Compression is turned on by adding the compressing code, not selectable at runtime.//
//// Comments by CR: This conditionally compiled section is used to when debugging the introduction// of a PCM-to-X codec into this system. Two files are created so that the original file is// kept and can be analyzed with the Microsoft Sound /Recorder Player. When the code is debugged,// remove the DEBUGGING_COMPRESSION sections.//
#if DEBUGGING_COMPRESSION// Comments by CR: Remove this section when done debugging compression//// Speech will expect to compress a PCM wav file named VOICE_FILE_PATH/VOICE_FILE_NAME,// and save it under the name VOICE_FILE_PATH/COMPRESSED_VOICE_FILE_NAMEchar szCompressedVoiceFilePath[256];sprintf(szCompressedVoiceFilePath,"%s%s",VOICE_FILE_PATH,COMPRESSED_VOICE_FILE_NAME);this->CompressSpeech(szCompressedVoiceFilePath);// Comments by CR: set the compressed voice filenamem_spTalkerClient->SetVoiceFilename(szCompressedVoiceFilePath);
#elsethis->CompressSpeech(szVoiceFilePath);// Comments by CR: set the voice filenamem_spTalkerClient->SetVoiceFilename(szVoiceFilePath);
#endif// Comments by CR: tell the talker to start sending the messageHRESULT hr = m_spTalkerClient->Talk();
#if DEBUGGING_COMPRESSION// Comments by CR: when completed, remove the file(s) to avoid sharing conflicts// NOTE: The compressed file could or could not be there depending if the codec is// installed or not. Try to delete the file for compatibility, and if it is not there// it fails but that is ok.DeleteFile(szCompressedVoiceFilePath);
#endif
135
// Comments by CR: when completed, remove the file(s) to avoid sharing conflicts// At least the WAV file should be there! Get rid of it.// BUT, do not delete the file if the message is being sent from a pre-recorded file.// If a pre-recorded message is being sent, just initialize the string.BOOL bStatus=FALSE;if ( strlen(m_szWavFileName) )
DWORD dError = GetLastError();hr = ! S_OK; // CR. could not delete file
}}return ( hr );
}
//******************************************************************************// Comments by CR: THe StarRecording method calls the AcmApp modules to init the// audio hardware, and puts the hardware in a recording mode//-----------------------------------------------------------------------------STDMETHODIMP CRecorder::StartRecording(){ // Comments by CR: Create the media (WAV) file
if ( AppFileNew(TRUE) == FALSE )return ( !S_OK );
// Comments by CR: OPen the hwif ( this->AcmAppPlayRecordOpen() == FALSE )
return ( !S_OK );
this->AcmAppPlayRecordStatus();
// Comments by CR: Init hw for recordingif ( this->AcmAppPlayRecordRecord() == FALSE )
return ( !S_OK );
this->AcmAppPlayRecordStatus();
// Comments by CR: when hw is ready, inform the client by firing the event!Fire_OnReadyToRecord();
return S_OK;}
//******************************************************************************// Comments by CR: THe StopRecording method calls the AcmApp modules to put the// audio record hardware in an idle mode//-----------------------------------------------------------------------------STDMETHODIMP CRecorder::StopRecording(){
if ( this->AcmAppPlayRecordStop() == FALSE )return ( !S_OK );
this->AcmAppPlayRecordStatus();
if ( this->AcmAppPlayRecordClose() == FALSE )return ( !S_OK );
m_fFileOpen = FALSE;
// Comments by CR: This section was added because the application is having some// problems synchronizing the media file. So before we let the file go, check// if recording was ok, so as not to send an empry file to the Voice Messaging Server{
Sleep(1000); //this is to allow file to close properly before checking its size
136
//*CR. Open the file to check the sizeHANDLE hf;char szVoiceFilePath[256];sprintf(szVoiceFilePath,"%s%s",VOICE_FILE_PATH,VOICE_FILE_NAME);hf = CreateFile(szVoiceFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,OPEN_EXISTING, 0, 0);
if (INVALID_HANDLE_VALUE == hf)return( !S_OK );
if ( GetFileSize((HANDLE)hf, NULL) ){
CloseHandle(hf);// Comments by CR: when hw is ready, inform the COM client using the connection pointFire_OnDoneRecording();return S_OK;
//******************************************************************************// Comments by CR: This is convenience method provided in the interface for future use// This method shall convert a UNcompressed PCM wav file named VOICE_FILE_PATH/// VOICE_FILE_NAME into a compressed wav file named VOICE_FILE_PATH/COMPRESSED_VOICE_FILE_NAME// THe compression used is determined by the code that gets called, so this is NOT the// compression code, but the entry point to the compression code.//-----------------------------------------------------------------------------STDMETHODIMP CRecorder::CompressSpeech(LPSTR pFileName){
// Comments by CR: Todo; call the compress routines from here
return S_OK;}
//==========================================================================;//////////==========================================================================;//******************************************************************************// Comments by CR: Following is the code adapted from the Microsoft's AcmApp. A lot// of code overhead was removed (user confirmation dialogs, compatiblity with Win16,// and others). This code contains the algorithms used to access the Microsoft's// Audio Compression Manager (ACM) to: create, open, store, and manage media WAV files,// using the Media Control Interface (mci).//-----------------------------------------------------------------------------
//--------------------------------------------------------------------------;//// BOOL AppFileSave//// Description:// This function handles the IDM_FILE_SAVE[AS] messages. It is// responsible for saving the current file. If a file name needs// to be specified then the save file dialog is displayed.//// Arguments:
137
// HWND hwnd: Handle to application window.//// PACMAPPFILEDESC m_paafd: Pointer to current file descriptor.//// BOOL fSaveAs: TRUE if the save file chooser should be displayed// before saving the file. FALSE if should operate like File.Save.//// Return (BOOL):// The return value is TRUE if the file was saved. It is FALSE if the// user canceled the operation or the file does not need saved.////--------------------------------------------------------------------------;
// // check if we should bring up the save file chooser dialog... // if (fSaveAs || (0 == lstrcmp(m_paafd->szFileTitle, m_szFileUntitled))) { // // get the file name for saving the data to into temporary // buffers (so if we fail to save it we can back out cleanly). // f = AppGetFileName(szFilePath, szFileTitle, APP_GETFILENAMEF_SAVE);//*CR. if (!f) return (FALSE); }
// // save the file... // f = AcmAppFileSave(hwnd, m_paafd, szFilePath, szFileTitle, 0); if (f) { // // changes have been saved, so clear the modified bit... // m_paafd->fdwState &= ~ACMAPPFILEDESC_STATEF_MODIFIED;
// Description:// This function is a wrapper for the Get[Open/Save]FileName commdlg// chooser dialogs. Based on the fuFlags argument, this function will// display the appropriate chooser dialog and return the result.//// Arguments:// HWND hwnd: Handle to parent window for chooser dialog.//// PTSTR pszFilePath: Pointer to buffer to receive the file path.//// PTSTR pszFileTitle: Pointer to buffer to receive the file title.// This argument may be NULL, in which case no title will be returned.//// UINT fuFlags://// Return (BOOL):// The return value is TRUE if a file was chosen. It is FALSE if the// user canceled the operation.//////--------------------------------------------------------------------------;
// // NOTE! building the filter string for the OPENFILENAME structure // is a bit more difficult when dealing with Unicode and C8's new // optimizer. it joyfully removes literal '\0' characters from // strings that are concatted together. if you try making each // string separate (array of pointers to strings), the compiler // will dword align them... etc, etc. // // if you can think of a better way to build the silly filter string // for common dialogs and still work in Win 16 and Win 32 [Unicode] // i'd sure like to hear about it... // for (pch = &szExtFilter[0]; '\0' != *pch; pch++) { if ('!' == *pch) *pch = '\0'; }
// // initialize the OPENFILENAME members // memset(&ofn, 0, sizeof(OPENFILENAME));
pszFilePath[0] = '\0'; if (pszFileTitle) pszFileTitle[0] = '\0';
// // if the fuFlags.APP_GETFILENAMEF_SAVE bit is set, then call // GetSaveFileName() otherwise call GetOpenFileName(). why commdlg was // designed with two separate functions for save and open only clark // knows. // if (fuFlags & APP_GETFILENAMEF_SAVE) { ofn.Flags = APP_OFN_FLAGS_SAVE; f = GetSaveFileName(&ofn); if (f) { if (NULL != pszFilePath) { lstrcpy(m_szInitialDirSave, pszFilePath);
pch = &m_szInitialDirSave[lstrlen(m_szInitialDirSave) - 1]; for ( ; m_szInitialDirSave != pch; pch--) { if ('\\' == *pch) { *pch = '\0'; break; } } } } } else { ofn.Flags = APP_OFN_FLAGS_OPEN; f = GetOpenFileName(&ofn); if (f) { if (NULL != pszFilePath) { lstrcpy(m_szInitialDirOpen, pszFilePath);
//--------------------------------------------------------------------------;//// BOOL AppTitle//// Description:// This function formats and sets the title text of the application's// window.//// Arguments:// HWND hwnd: Handle to application window to set title text for.//// PTSTR pszFileTitle: Pointer to file title to display.//// Return (BOOL):// The return value is always TRUE.//////--------------------------------------------------------------------------;
MEditPrintF(hedit, TEXT("%25s: %u"), (LPTSTR)TEXT("Bits Per Sample"), pwfx->wBitsPerSample);
if (WAVE_FORMAT_PCM != pwfx->wFormatTag) { AppFormatBigNumber(ach, pwfx->cbSize); MEditPrintF(hedit, TEXT("%25s: %s bytes\r\n"), (LPTSTR)TEXT("Extra Format Information"), (LPTSTR)ach);
AcmAppDumpExtraHeaderData(hedit, pwfx); }
// // note that we do NOT set the 'WAVE_ALLOWSYNC' bit on queries because // the player/recorder dialog uses MCIWAVE--which cannot work with // SYNC devices. // mmr = waveOutOpen(NULL, m_uWaveOutId,#if (WINVER < 0x0400) (LPWAVEFORMAT)pwfx,#else pwfx,#endif 0L, 0L, WAVE_FORMAT_QUERY);
fCanPlayRecord = (MMSYSERR_NOERROR == mmr);
if (!fCanPlayRecord) { // // this situation can happen with the 'preferred' device settings // for the Sound Mapper. // mmr = waveInOpen(NULL, m_uWaveInId,#if (WINVER < 0x0400) (LPWAVEFORMAT)pwfx,#else pwfx,#endif 0L, 0L, WAVE_FORMAT_QUERY);
//--------------------------------------------------------------------------;//// void AppHourGlass//// Description:// This function changes the cursor to that of the hour glass or// back to the previous cursor.//// This function can be called recursively.//// Arguments:// BOOL fHourGlass: TRUE if we need the hour glass. FALSE if we need// the arrow back.//// Return (void):// On return, the cursor will be what was requested.////--------------------------------------------------------------------------;
// // get the name for the format tag of the specified format // if (NULL != pszFormatTag) { ACMFORMATTAGDETAILS aftd;
// // initialize all unused members of the ACMFORMATTAGDETAILS // structure to zero // memset(&aftd, 0, sizeof(aftd));
// // fill in the required members of the ACMFORMATTAGDETAILS // structure for the ACM_FORMATTAGDETAILSF_FORMATTAG query // aftd.cbStruct = sizeof(aftd); aftd.dwFormatTag = pwfx->wFormatTag;
// // ask the ACM to find the first available driver that // supports the specified format tag // mmr = acmFormatTagDetails(NULL, &aftd, ACM_FORMATTAGDETAILSF_FORMATTAG); if (MMSYSERR_NOERROR == mmr) { // // copy the format tag name into the caller's buffer // lstrcpy(pszFormatTag, aftd.szFormatTag); } else { PTSTR psz;
// // no ACM driver is available that supports the // specified format tag //
f = FALSE; psz = NULL;
// // the following stuff if proof that the world does NOT need // yet another ADPCM algorithm!! // switch (pwfx->wFormatTag) { case WAVE_FORMAT_UNKNOWN: psz = TEXT("** RESERVED INVALID TAG **"); break;
case WAVE_FORMAT_PCM: psz = TEXT("PCM"); break;
case WAVE_FORMAT_ADPCM: psz = TEXT("Microsoft ADPCM");
147
break;
case 0x0003: psz = TEXT("MV's *UNREGISTERED* ADPCM"); break;
case WAVE_FORMAT_IBM_CVSD: psz = TEXT("IBM CVSD"); break;
case WAVE_FORMAT_ALAW: psz = TEXT("A-Law"); break;
case WAVE_FORMAT_MULAW: psz = TEXT("u-Law"); break;
case WAVE_FORMAT_OKI_ADPCM: psz = TEXT("OKI ADPCM"); break;
case WAVE_FORMAT_IMA_ADPCM: psz = TEXT("IMA/DVI ADPCM"); break;
case WAVE_FORMAT_DIGISTD: psz = TEXT("DIGI STD"); break;
case WAVE_FORMAT_DIGIFIX: psz = TEXT("DIGI FIX"); break;
case WAVE_FORMAT_YAMAHA_ADPCM: psz = TEXT("Yamaha ADPCM"); break;
case WAVE_FORMAT_SONARC: psz = TEXT("Sonarc"); break;
case WAVE_FORMAT_DSPGROUP_TRUESPEECH: psz = TEXT("DSP Group TrueSpeech"); break;
case WAVE_FORMAT_ECHOSC1: psz = TEXT("Echo SC1"); break;
case WAVE_FORMAT_AUDIOFILE_AF36: psz = TEXT("Audiofile AF36"); break;
case WAVE_FORMAT_CREATIVE_ADPCM: psz = TEXT("Creative Labs ADPCM"); break;
case WAVE_FORMAT_APTX: psz = TEXT("APTX"); break;
case WAVE_FORMAT_AUDIOFILE_AF10: psz = TEXT("Audiofile AF10"); break;
case WAVE_FORMAT_DOLBY_AC2: psz = TEXT("Dolby AC2");
148
break;
case WAVE_FORMAT_MEDIASPACE_ADPCM: psz = TEXT("Media Space ADPCM"); break;
case WAVE_FORMAT_SIERRA_ADPCM: psz = TEXT("Sierra ADPCM"); break;
case WAVE_FORMAT_G723_ADPCM: psz = TEXT("CCITT G.723 ADPCM"); break;
case WAVE_FORMAT_GSM610: psz = TEXT("GSM 6.10"); break;
case WAVE_FORMAT_G721_ADPCM: psz = TEXT("CCITT G.721 ADPCM"); break;
case WAVE_FORMAT_DEVELOPMENT: psz = TEXT("** RESERVED DEVELOPMENT ONLY TAG **"); break;
if (NULL != psz) { lstrcpy(pszFormatTag, psz); } } }
// // get the description of the attributes for the specified // format // if (NULL != pszFormat) { ACMFORMATDETAILS afd;
// // initialize all unused members of the ACMFORMATDETAILS // structure to zero // memset(&afd, 0, sizeof(afd));
// // fill in the required members of the ACMFORMATDETAILS // structure for the ACM_FORMATDETAILSF_FORMAT query // afd.cbStruct = sizeof(afd); afd.dwFormatTag = pwfx->wFormatTag; afd.pwfx = pwfx;
// // the cbwfx member must be initialized to the total size // in bytes needed for the specified format. for a PCM // format, the cbSize member of the WAVEFORMATEX structure // is not valid. // if (WAVE_FORMAT_PCM == pwfx->wFormatTag) {
// // ask the ACM to find the first available driver that // supports the specified format // mmr = acmFormatDetails(NULL, &afd, ACM_FORMATDETAILSF_FORMAT); if (MMSYSERR_NOERROR == mmr) { // // copy the format attributes description into the caller's // buffer // lstrcpy(pszFormat, afd.szFormat); } else { TCHAR ach[2]; TCHAR szChannels[24]; UINT cBits;
// // no ACM driver is available that supports the // specified format //
f = FALSE;
// // // ach[0] = gchIntlList; ach[1] = '\0';
gchIntlList = ach[0];
ach[0] = gchIntlDecimal; ach[1] = '\0';
gchIntlDecimal = ach[0];
// // compute the bit depth--this _should_ be the same as // wBitsPerSample, but isn't always... // cBits = (UINT)(pwfx->nAvgBytesPerSec * 8 / pwfx->nSamplesPerSec / pwfx->nChannels);
if ((1 == pwfx->nChannels) || (2 == pwfx->nChannels)) { if (1 == pwfx->nChannels) lstrcpy(szChannels, TEXT("Mono")); else lstrcpy(szChannels, TEXT("Stereo"));
// Comments by CR: Believe it or not, THIS IS Microsoft code (+ comments) !!! // // !!! this is really horrible code !!! //{ #define ACMAPP_DUMP_BYTES_PER_LINE 16
UINT u; UINT v;
for (u = 0; u < pwfx->cbSize; u += ACMAPP_DUMP_BYTES_PER_LINE) {
151
MEditPrintF(hedit, TEXT("~0x%.04X"), u);
for (v = 0; v < ACMAPP_DUMP_BYTES_PER_LINE; v++) { if ((u + v) >= pwfx->cbSize) break;
//--------------------------------------------------------------------------;//// BOOL AcmAppFileSave//// Description:// This function saves the file to the specified file.//// NOTE! This function does NOT bring up a save file chooser dialog// if the file path is invalid. The calling function is responsible// for making sure the file path is valid before calling this function.//// This function also does NOT modify the 'modified' bit of the file// descriptor. This is up to the calling function.//// Arguments:// HWND hwnd: Handle to main window.//// PACMAPPFILEDESC m_paafd: Pointer to file descriptor.//// Return (BOOL):// The return value is TRUE if the function is successful. It is FALSE// if an error occurred. If an error does occur, then the contents// of the file descriptor was not saved.////--------------------------------------------------------------------------;
//--------------------------------------------------------------------------;//// int MEditPrintF//// Description:// This function is used to print formatted text into a Multiline// Edit Control as if it were a standard console display. This is// a very easy way to display small amounts of text information// that can be scrolled and copied to the clip-board.//
152
// Arguments:// HWND hedit: Handle to a Multiline Edit control.//// PTSTR pszFormat: Pointer to any valid format for wsprintf. If// this argument is NULL, then the Multiline Edit Control is cleared// of all text.////// Return (int):// Returns the number of characters written into the edit control.//// Notes:// The pszFormat string can contain combinations of escapes that// modify the default behaviour of this function. Escapes are single// character codes placed at the _beginning_ of the format string.//// Current escapes defined are://// ~ : Suppresses the default CR/LF added to the end of the// printed line. Since the most common use of this function// is to output a whole line of text with a CR/LF, that is// the default.//// ` : Suppresses logging to the debug terminal (regardless of// the global debug log options flag).//////--------------------------------------------------------------------------;
// // if the pszFormat argument is NULL. // if (NULL == pszFormat) { AcmAppDebugLog(NULL);
return (0); }
// // format and display the string in the window... first search for // escapes to modify default behaviour. // for (;;) { switch (*pszFormat)
153
{ case '~': fCRLF = FALSE; pszFormat++; continue;
case '`': fDebugLog = FALSE; pszFormat++; continue; }
break; }
va_start(va, pszFormat);#ifdef WIN32 n = wvsprintf(ach, pszFormat, va);#else n = wvsprintf(ach, pszFormat, (LPSTR)va);#endif va_end(va);
if (fDebugLog) { AcmAppDebugLog(ach); }
if (fCRLF) { if (fDebugLog) { AcmAppDebugLog(szCRLF); } }
return (n);} // MEditPrintF()
//--------------------------------------------------------------------------;//// void AcmAppDebugLog//// Description:// This function logs information to the debugger if the Debug Log// option is set. You can then run DBWin (or something similar)// to redirect the output whereever you want. Very useful for debugging// ACM drivers.//// Arguments:// PTSTR pszFormat: Pointer to any valid format for wsprintf.//// Return (void):// None.////--------------------------------------------------------------------------;
// // format and display the string in a message box... // va_start(va, pszFormat);#ifdef WIN32 wvsprintf(ach, pszFormat, va);#else wvsprintf(ach, pszFormat, (LPSTR)va);#endif va_end(va);
OutputDebugString(ach); }} // AcmAppDebugLog()
//--------------------------------------------------------------------------;//// BOOL AcmAppFileNew//// Description://// Arguments:// HWND hwnd: Handle to main window.//// PACMAPPFILEDESC m_paafd: Pointer to file descriptor.//// Return (BOOL):////--------------------------------------------------------------------------;
// Comments by CR: This where the WAV file parameters (sample rate, etc) is setuppwfx->wFormatTag = PCM_FORMAT_TAG;pwfx->nChannels = PCM_CHANNELS;pwfx->nSamplesPerSec = PCM_SAMPLES_PER_SEC;
// // create the RIFF chunk of form type 'WAVE' // ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E'); ckRIFF.cksize = 0L; mmioCreateChunk(hmmio, &ckRIFF, MMIO_CREATERIFF);
// // now create the destination fmt, fact, and data chunks _in that order_ // // hmmio is now descended into the 'RIFF' chunk--create the format chunk // and write the format header into it // cbwfx = SIZEOF_WAVEFORMATEX(pwfx);
// // create the 'fact' chunk (not necessary for PCM--but is nice to have) // since we are not writing any data to this file (yet), we set the // samples contained in the file to 0.. // ck.ckid = mmioFOURCC('f', 'a', 'c', 't'); ck.cksize = 0L; mmioCreateChunk(hmmio, &ck, 0);
//--------------------------------------------------------------------------;//// BOOL AcmAppFileOpen//// Description:// This function opens the specified file and get the important info// from it.//// NOTE! This function does NOT check for a modified file! It is// assumed that the calling function took care of everything before// calling this function.//// Arguments:// HWND hwnd: Handle to main window.//// PACMAPPFILEDESC m_paafd: Pointer to file descriptor.//// Return (BOOL):// The return value is TRUE if the function is successful. It is FALSE// if an error occurred. If an error does occur, then the contents// of the file descriptor will remain unchanged.//////--------------------------------------------------------------------------;
// // now return the fully qualified path and title for the file //#ifndef WIN32 lstrcpy(m_paafd->szFilePath, of.szPathName);#endif AppGetFileTitle(m_paafd->szFilePath, m_paafd->szFileTitle);
// This function extracts the file title from a file path and returns// it in the caller's specified buffer.//// Arguments:// PTSTR pszFilePath: Pointer to null terminated file path.//// PTSTR pszFileTitle: Pointer to buffer to receive the file title.//// Return (BOOL):// Always returns TRUE. But should return FALSE if this function// checked for bogus values, etc.//////--------------------------------------------------------------------------;
// // scan to the end of the file path string.. // for (pch = pszFilePath; '\0' != *pch; pch++) ;
// // now scan back toward the beginning of the string until a slash (\), // colon, or start of the string is encountered. // while ((pch >= pszFilePath) && !IS_SLASH(*pch) && (':' != *pch)) { pch--; }
// // finally, copy the 'title' into the destination buffer.. skip ahead // one char since the above loop steps back one too many chars... // lstrcpy(pszFileTitle, ++pch);
// // first try to open the file, etc.. open the given file for reading // using buffered I/O // hmmio = mmioOpen((LPTSTR)pszFilePath, NULL, MMIO_READ | MMIO_ALLOCBUF); if (NULL == hmmio) goto wio_Open_Error;
pwio->hmmio = hmmio;
// // locate a 'WAVE' form type... // ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E'); if (mmioDescend(hmmio, &ckRIFF, NULL, MMIO_FINDRIFF)) goto wio_Open_Error;
// // we found a WAVE chunk--now go through and get all subchunks that // we know how to deal with... // pwio->dwDataSamples = (DWORD)-1L;
#if 0 if (lrt=riffInitINFO(&wio.pInfo)) { lr=lrt; goto wio_Open_Error; }#endif
// // // while (MMSYSERR_NOERROR == mmioDescend(hmmio, &ck, &ckRIFF, 0)) { // // quickly check for corrupt RIFF file--don't ascend past end! // if ((ck.dwDataOffset + ck.cksize) > (ckRIFF.dwDataOffset + ckRIFF.cksize))
162
{// Comments by CR: Believe it or not, THIS IS Microsoft's code !!!werr = WIOERR_BADFILE;
case mmioFOURCC('D', 'I', 'S', 'P'):#if 0 riffReadDISP(hmmio, &ck, &(wio.pDisp));#endif break;
case mmioFOURCC('f', 'm', 't', ' '): // // !?! another format chunk !?! // if (NULL != pwio->pwfx) break;
// // get size of the format chunk, allocate and lock memory // for it. we always alloc a complete extended format header // (even for PCM headers that do not have the cbSize field // defined--we just set it to zero). // dw = ck.cksize; if (dw < sizeof(WAVEFORMATEX)) dw = sizeof(WAVEFORMATEX);
pwio->pwfx = (LPWAVEFORMATEX) new WAVEFORMATEX[dw]; if (NULL == pwio->pwfx) { werr = WIOERR_NOMEM; goto wio_Open_Error; }
// // read the format chunk // werr = WIOERR_FILEERROR; dw = ck.cksize; if (mmioRead(hmmio, (HPSTR)pwio->pwfx, dw) != (LONG)dw) goto wio_Open_Error; break;
case mmioFOURCC('d', 'a', 't', 'a'): // // !?! multiple data chunks !?! // if (0L != pwio->dwDataBytes) break;
163
// // just hang on to the total length in bytes of this data // chunk.. and the offset to the start of the data // pwio->dwDataBytes = ck.cksize; pwio->dwDataOffset = ck.dwDataOffset; break;
case mmioFOURCC('f', 'a', 'c', 't'): // // !?! multiple fact chunks !?! // if (-1L != pwio->dwDataSamples) break;
// // read the first dword in the fact chunk--it's the only // info we need (and is currently the only info defined for // the fact chunk...) // // if this fails, dwDataSamples will remain -1 so we will // deal with it later... // mmioRead(hmmio, (HPSTR)&pwio->dwDataSamples, sizeof(DWORD)); break; }
// // step up to prepare for next chunk.. // mmioAscend(hmmio, &ck, 0); }
// // if no fmt chunk was found, then die! // if (NULL == pwio->pwfx) { werr = WIOERR_ERROR; goto wio_Open_Error; }
// // all wave files other than PCM are _REQUIRED_ to have a fact chunk // telling the number of samples that are contained in the file. it // is optional for PCM (and if not present, we compute it here). // // if the file is not PCM and the fact chunk is not found, then fail! // if (-1L == pwio->dwDataSamples) { if (WAVE_FORMAT_PCM == pwio->pwfx->wFormatTag) { pwio->dwDataSamples = pwio->dwDataBytes / pwio->pwfx->nBlockAlign; } else {
// Comments by CR: Believe it or not, THIS IS Microsoft's code (+ comments) !!! // // !!! HACK HACK HACK !!! // // although this should be considered an invalid wave file, we // will bring up a message box describing the error--hopefully // people will start realizing that something is missing??? // werr = WIOERR_BADFILE; goto wio_Open_Error;
164
// // !!! need to hack stuff in here !!! // pwio->dwDataSamples = 0L; } }
// // cool! no problems.. // return (WIOERR_NOERROR);
// // return error (after minor cleanup) //wio_Open_Error:
WIOERR CRecorder:: wioFileClose( LPWAVEIOCB pwio, DWORD fdwClose){ // // validate a couple of things... // if (NULL == pwio) return (WIOERR_BADPARAM);
// // get rid of stuff... //// wioStopWave(pwio);
if (NULL != pwio->hmmio) { mmioClose(pwio->hmmio, 0); }
// FreeWaveHeaders(lpwio);
#if 0 if (pwio->pInfo) riffFreeINFO(&(lpwio->pInfo));
165
if (pwio->pDisp) riffFreeDISP(&(lpwio->pDisp));#endif
return (WIOERR_NOERROR);} // wioFileClose()
//--------------------------------------------------------------------------;//// BOOL AppFileNew//// Description:// This function is called to handle the IDM_FILE_NEW message. It is// responsible for clearing the working area for a new unnamed file.//// Arguments:// HWND hwnd: Handle to application window.//// PACMAPPFILEDESC m_paafd: Pointer to current file descriptor.//// Return (BOOL):// The return value is TRUE if the working area was cleared and is// ready for new stuff. The return value is FALSE if the user canceled// the operation.////--------------------------------------------------------------------------;
if (fCreate) { f = AcmAppFileNew();//*CR. if (!f) return (FALSE); } else { // // if there is currently a file path, then we have to do some real // work... // if ('\0' != m_paafd->szFilePath[0]) {
f = AcmAppFileNew();//*CR. if (!f) return (FALSE); }
// // blow away the old file path and title; set the window title // and return success // lstrcpy(m_paafd->szFilePath, m_szFileUntitled); lstrcpy(m_paafd->szFileTitle, m_szFileUntitled); }
// BOOL AppFileOpen//// Description:// This function handles the IDM_FILE_OPEN message. It is responsible// for getting a new file name from the user and opening that file// if possible.//// Arguments:// HWND hwnd: Handle to application window.//// PACMAPPFILEDESC m_paafd: Pointer to current file descriptor.//// Return (BOOL):// The return value is TRUE if a new file was selected and opened.// It is FALSE if the user canceled the operation.////--------------------------------------------------------------------------;
// // get the file name of the new file into temporary buffers (so // if we fail to open it we can back out cleanly). // f = AppGetFileName(szFilePath, szFileTitle, APP_GETFILENAMEF_OPEN);// Comments by CR: Added if (!f) return (FALSE);
//!!! // read the new file... // lstrcpy(m_paafd->szFilePath, szFilePath); lstrcpy(m_paafd->szFileTitle, szFileTitle);
f = AcmAppFileOpen();// Comments by CR: Added if (f) { // // set the window title text... //
// Comments by CR: Added AppTitle(szFileTitle); AcmAppDisplayFileProperties(); }
//--------------------------------------------------------------------------;//// MCIERROR AcmPlayRecordSendCommand//// Description:// The string is of the form "verb params" our device name is inserted// after the verb and send to the device.//// Arguments:// HWND hwnd://// PSTR pszCommand://// PSTR pszReturn://// UINT cbReturn://// BOOL fErrorBox://// Return (MCIERROR)://////--------------------------------------------------------------------------;
TEXT(""), TEXT("record insert !"), TEXT("record overwrite !"), TEXT("record to Y !"), TEXT("record from X to Y !"),
TEXT(""), TEXT("resume"),
TEXT(""), TEXT("save"), TEXT("save FILENAME"),
TEXT(""), TEXT("seek to Y"), TEXT("seek to start"), TEXT("seek to end"),
TEXT(""), TEXT("set alignment X"), TEXT("set any input"), TEXT("set any output"), TEXT("set audio all off"), TEXT("set audio all on"), TEXT("set audio left off"), TEXT("set audio left on"), TEXT("set audio right off"), TEXT("set audio right on"), TEXT("set bitspersample X"), TEXT("set bytespersec X"), TEXT("set channels X"), TEXT("set format tag X"), TEXT("set format tag pcm"), TEXT("set input X"), TEXT("set output X"), TEXT("set samplespersec X"), TEXT("set time format bytes"), TEXT("set time format milliseconds"), TEXT("set time format samples"),
TEXT(""), TEXT("status alignment"), TEXT("status bitspersample"), TEXT("status bytespersec"), TEXT("status channels"), TEXT("status current track"), TEXT("status format tag"), TEXT("status input"), TEXT("status length"), TEXT("status length track X"), TEXT("status level"), TEXT("status media present"), TEXT("status mode"), TEXT("status number of tracks"), TEXT("status output"), TEXT("status position"), TEXT("status position track X"), TEXT("status ready"), TEXT("status samplespersec"), TEXT("status start position"), TEXT("status time format"),
//*****************************************************// stdafx.cpp : source file that includes just the standard includes// stdafx.pch will be the pre-compiled header// stdafx.obj will contain the pre-compiled type information//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Record.dll workspace. No changes were made by the author.//-----------------------------------------------------
#include "stdafx.h"
#ifdef _ATL_STATIC_REGISTRY#include <statreg.h>
177
#include <statreg.cpp>#endif
#include <atlimpl.cpp>
178
B.1.3 VoiceSender Dll
// stdafx.h : include file for standard system include files,// or project specific include files that are used frequently,// but are changed infrequently
#include <atlbase.h>//You may derive a class from CComModule and use it if you want to override//something, but do not change the name of _Moduleextern CComModule _Module;#include <atlcom.h>
//{{AFX_INSERT_LOCATION}}// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
//*****************************************************// Talker.h : Declaration of the CTalker//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the VoiceSender.dll workspace. Changes made by the author to the// file are marked by 'Comments by CR:'.// This is the header file of the CTalker class. CTalker is// the C++ dependent implementation of the Talker coclass.////-----------------------------------------------------#ifndef __TALKER_H_#define __TALKER_H_
public CComObjectRootEx<CComSingleThreadModel>,public CComCoClass<CTalker, &CLSID_Talker>,public ITalker
{public:
// Comments by CR: for m_nPakcetSize// The voice/sound file is sent in chunks ranging from:// 1 to 576 bytes. 576 bytes is the minimum MTU required by all IPv4
179
// implementations. This size can be varied to arrive at different, to// determine the one that allows the best delivery time performance.CTalker():m_pVoiceDataBuffer(0),m_clientSocket(INVALID_SOCKET),m_nPacketSize(576){
memset(m_szVoiceFilename,NULL,256);}
HRESULT FinalConstruct(){
int status;
/* initialize the Windows Socket DLL */status=WSAStartup(MAKEWORD(1, 1), &m_Data);if ( status )
return( !S_OK);
/* zero the sockaddr_in structure */memset(&m_serverSockAddr, 0,sizeof(m_serverSockAddr));
UINT m_nPacketSize;// Comments by CR: Socket cleanup is a convenience function used to close the socket after it has been// usedvoid SocketCleanup(void);
//*****************************************************// stdafx.cpp : source file that includes just the standard includes// stdafx.pch will be the pre-compiled header// stdafx.obj will contain the pre-compiled type information//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the VoiceSender.dll workspace. No changes were made by the author.//-----------------------------------------------------
//*****************************************************// Talker.cpp : Implementation of CTalker//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the VoiceSender.dll workspace. Changes made by the author to the// file are marked by 'Comments by CR:'.// CTalker the C++ dependent implementation of the Talker coclass.//// Talker is a COM component that implements a TCP socket client to// send a voice/sound file to a TCP socket server.// It uses the WinSock 2.0 API.//-----------------------------------------------------#include "stdafx.h"#include "VoiceSender.h"#include "Talker.h"
// Comments by CR: This is the Set method the COM client calls for the telling the// Talker which IP address and socket number it is to send the voice/sound file to.//STDMETHODIMP CTalker::SetListenerAddress(LPSTR pServerSockAddr, LPSTR pServerSockPort){
// Comments by CR: specify the address family as Internet//m_serverSockAddr.sin_family=AF_INET;
return S_OK;}
// Comments by CR: This is the Set method the COM client calls for the telling the// Talker the name of the file voice/sound file it will send.//STDMETHODIMP CTalker::SetVoiceFilename(LPSTR pFilename){
// Comments by CR: ONly change the packet size if the value is valid, else leave the default// which is initialized in the constructor method.if ( nPacketSize > 0 )
this->m_nPacketSize = nPacketSize;
return S_OK;}
// Comments by CR: Talk() is called by the COM client to trigger the socket client to// connect to the socket server, and transmit the voice/sound file.// The file transfer uses the following protocol:// Client sends [number of bytes in file (6 bytes)]// Server acks// Client sends [file data chunks (X bytes)]// Server acks each chunk, and client//STDMETHODIMP CTalker::Talk(){
int status = 0;
// Comments by CR: make sure that sockets have been intialized first//if ( m_serverSockAddr.sin_port && m_serverSockAddr.sin_addr.s_addr ){
// Comments by CR: Open the file and read it in buffer//
// Comments by CR: Bind the client socket to the client address//status = bind(m_clientSocket,(LPSOCKADDR) &clientSockAddr,sizeof(clientSockAddr));if ( status == SOCKET_ERROR ){
this->SocketCleanup();return( !S_OK );
}
// Comments by CR: Connect the client socket to the server socket//status = connect( m_clientSocket, (LPSOCKADDR) &m_serverSockAddr, sizeof(m_serverSockAddr) );if ( status == SOCKET_ERROR ){
this->SocketCleanup();return( !S_OK );
}
// Comments by CR: Transfer file in chunkschar szFileSize[7];memset(szFileSize,NULL,7);sprintf(szFileSize,"%06X",nNumberOfBytesRead);status = send (m_clientSocket,szFileSize,6,0); //CR. 1st send the file size
// Comments by CR: Send the file contents, using a predertmined chunk size,// or setup from the COM client (as permissible by the min IPv4 MTU size).if ( nNumberOfBytesRead > (this->m_nPacketSize) ){
// Comments by CR: the file size is larger than the chunk size, so send the// file in chunks.for (int i=0; (DWORD) i<nNumberOfBytesRead;i+=(this->m_nPacketSize)){
// Comments by CR: Check if the number of bytes left is less than the// chunk, then send those leftif ( (nNumberOfBytesRead-i) < (this->m_nPacketSize) )
status =send(m_clientSocket,&m_pVoiceDataBuffer[i],nNumberOfBytesRead-i,0);
// Comments by CR: the file size is less or equal to the chunk size so send it in// one shotstatus = send(m_clientSocket,m_pVoiceDataBuffer,nNumberOfBytesRead,0);
// Comments by CR: Status will contain the number of bytes sent, and if error, closesocket// and fail the transferif ( status == SOCKET_ERROR ){
this->SocketCleanup();return( !S_OK );
}
// Comments by CR: Clean up socket, and return okthis->SocketCleanup();return ( S_OK );
}return( !S_OK );
}
// Comments by CR: Socket cleanup convenience method. It deallocates the voice data// buffer and disconnects from the server.void CTalker::SocketCleanup(void){
// Comments by CR: Make sure all dynamic data is deallocatedif (m_pVoiceDataBuffer){
}// Comments by CR: shutodwn the connection to the client, and return errorshutdown(m_clientSocket,SD_BOTH);closesocket(m_clientSocket);
}
//*****************************************************// VoiceSender.cpp : Implementation of DLL Exports.//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the VoiceSender.dll workspace. No changes were made to the// automatically generated version.// VoiceSender.dll is the COM server DLL that provides the interfaces// required to transfer a voice/sound file to a TCP socket server.// It implements a TCP socket client, using the Winsock 2.0 API.//-----------------------------------------------------
// Note: Proxy/Stub Information// To build a separate proxy/stub DLL,// run nmake -f VoiceSenderps.mk in the project directory.
//*****************************************************// VoiceSender.idl : IDL source for VoiceSender.dll//
// This file will be processed by the MIDL tool to// produce the type library (VoiceSender.tlb) and marshalling code.//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet
185
// A Master of Science Thesis Presented to the Electrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the VoiceSender.dll workspace. Changes made by the author to the// file are marked by 'Comments by CR:'.// VoiceSender.dll is the COM server DLL that provides the interfaces// required to transfer the voice/sound file from the client to the server.// This file provides the interface definition language (idl)// for the interfaces exposed by coclasses in the VoiceSender.dll.//-----------------------------------------------------
import "oaidl.idl";import "ocidl.idl";
// Comments by CR: ITalker is the main interface which receives the socket server IP and socket,// along with the voice/sound file name.[
// Comments by CR: The Talker coclass exposes the ITalker interfacecoclass Talker{
[default] interface ITalker;};
};
186
B.2 Voice Messaging Server Source Code
B.2.1 VmsConsole Exe
//*****************************************************// Main.cpp : Main for the VmsConsole application//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This is the source code of the VmsConsole app. It implements// a Windows TCP socket server via port PORT. Also, it uses// a simple but powerful mechanism to notifies the user when// an instant voice message has arrived, and routes the message to// the COM client it uses for message playback.//-----------------------------------------------------#include "resource.h" // main symbols#include <winsock.h> //*CR. WinSock API#include <atlbase.h> //CR. COM support#include <time.h>
// Comments by CR: Import the library of the SpeechPlayer component#import "Playback.tlb" raw_interfaces_only, no_namespace, named_guids
#define PORT (u_short) 6300 //CR.#define NUM_COUNT_CHARACTERS 6#define NO_FLAGS_SET 0
// Comments by CR: Socket parameters. GLobals, but that is ok. Who's counting?WSADATA m_Data;SOCKADDR_IN m_serverSockAddr;SOCKADDR_IN m_clientSockAddr;SOCKET m_serverSocket;SOCKET m_clientSocket;
// Function prototypesint initSocketServer(void);int waitForClientConnection(void);int talkToClient(void);
//CR: main() : main function that implements the socket server, and uses the SpeechPlayer component// for playing back the message. The software is designed to work with a message queue of ONE, so// everytime a new message arrives it overwrites the previous one. When a message arrives, the user// is prompted to enter:// P for message playback,// Q for quit application (no more messages will be received),// W for waiting for another message. If W is selected before selecting P, the received// message is ignored and will be lost.// Because this implements a TCP socket server to receive instant// voice messages, it is not far fetched to attempt to implement this for a Windows CE device.// Sections of the code have been marked as code compatible with Windows CE.//int main(void){
printf("2.2.1 - 4/21/2000\nStarted VmsApp. This a demo interface for an instant voice messaging over IPplayback.\nPlease send your comments to [email protected]. Thanks.\n");
HRESULT hr = CoInitialize(NULL); // For CE, use CoInitializeEx()if ( hr != S_OK )
187
return (hr);
int status = 0;/* initialize the Windows Socket DLL */status=WSAStartup(MAKEWORD(1, 1), &m_Data); //Supported in CE!if ( status )
return(FALSE);
//CR. Infinite loop until app is killed, and there is no error with statuswhile(TRUE && !status ){
// Init socket serverprintf("InitSocketServer\n");status = initSocketServer();if ( status )
printf("Broke after iniSocketServer. Status = %d.\n",status);
//CR. wait for socket connection, if fails print error message and abortprintf("\n\nWaiting for client connection\n");status = waitForClientConnection();if ( status ){
printf("Broke after waiting for connection. Status = %d.\n",status);break;
}
// Connection was accepted by the server, now receive the dataprintf("Talking to client\n");
printf("Broke after talking to client. Status = %d.\n",status);break;
}
// When entire message is received, shutdown the socket connectionprintf("\t\tMessage received. Shutdown client connection\n");status=shutdown(m_clientSocket, 2); //Supported in CE!if (status == SOCKET_ERROR){
printf("Broke after shutdown socket. Status = %d.\n",status);break;
}
/* close the socket */status=closesocket(m_clientSocket); //Supported in CE!if (status == SOCKET_ERROR){
printf("Broke after closing socket. Status = %d.\n",status);break;
}
//CR. Break servershutdown(m_serverSocket, 2); //Supported in CE!closesocket(m_serverSocket); //Supported in CE!
endTime = clock();elapsedTime = (double) (endTime-startTime)/CLOCKS_PER_SEC;printf("\nMessage reception time = %3.3f.\n",elapsedTime);
188
//CR. Prompt the user that message arrivedchar line[256];do{
memset(line,NULL,sizeof(line));printf( "Voice message was received, Want to Playback or Wait for another or Quit? (P, W, Quit): "
);gets( line );
if (line[0] == 'P' || line[0] == 'p' ){
// CR: User accepts to playback the message, now connect to the SpeechPlayerCComQIPtr<ISpeechPlayer> m_spSpeechPlayer; //Supported in CE! >= 2.0hr = (LRESULT) m_spSpeechPlayer.CoCreateInstance (__uuidof(SpeechPlayer));
//Supported in CE! >= 2.0if ( hr == S_OK ){
printf( "\nMessage is being played back\n" );m_spSpeechPlayer->StartPlayback();m_spSpeechPlayer.Release();
}else{
printf( "\nError accessing voice player. Error code is %d. Exiting\n",hr );line[0] = 'Q'; //CR. force it to bail outbreak;
// CR: INitialize the server socket to the port, and addressint initSocketServer(){
int status=0;
/* zero the sockaddr_in structure */memset(&m_serverSockAddr, 0,sizeof(m_serverSockAddr));/* specify the port portion of the address */m_serverSockAddr.sin_port=htons(PORT);/* specify the address family as Internet */m_serverSockAddr.sin_family=AF_INET;/* specify that the address does not matter */m_serverSockAddr.sin_addr.s_addr=htonl(INADDR_ANY);
/* create a socket */m_serverSocket=socket(AF_INET, SOCK_STREAM, 0); //Supported in CE!if (m_serverSocket == INVALID_SOCKET){
int nError = WSAGetLastError();printf("Broke after creating socket. WSAGetLastError returns = %d.\n",nError);switch (nError){
case WSANOTINITIALISED:printf("\tWSANOTINITIALISED\n");
189
break;case WSAENETDOWN:
printf("\tWSAENETDOWN\n");break;
case WSAEAFNOSUPPORT:printf("\tWSAEAFNOSUPPORT\n");break;
case WSAEINPROGRESS:printf("\tWSAEINPROGRESS\n");break;
case WSAEMFILE:printf("\tWSAEMFILE\n");break;
case WSAENOBUFS:printf("\tWSAENOBUFS\n");break;
case WSAEPROTONOSUPPORT:printf("\tWSAEPROTONOSUPPORT\n");break;
case WSAEPROTOTYPE:printf("\tWSAEPROTOTYPE\n");break;
case WSAESOCKTNOSUPPORT:printf("\tWSAESOCKTNOSUPPORT\n");break;
default:printf("\tUnknown error\n");
}return (nError);
}/* associate the socket with the address */status=bind(m_serverSocket,(LPSOCKADDR) &m_serverSockAddr,sizeof(m_serverSockAddr)); //Supported in CE!if ( status ){
int nError = WSAGetLastError();printf("Broke after binding. WSAGetLastError returns = %d.\n",nError);switch (nError){
case WSANOTINITIALISED:printf("\tWSANOTINITIALISED\n");break;
case WSAENETDOWN:printf("\tWSAENETDOWN\n");break;
case WSAEADDRINUSE:printf("\tWSAEADDRINUSE \n");break;
case WSAEADDRNOTAVAIL:printf("\tWSAEADDRNOTAVAIL\n");break;
case WSAEFAULT:printf("\tWSAEFAULT\n");break;
case WSAEINPROGRESS:printf("\tWSAEINPROGRESS\n");break;
case WSAEINVAL:printf("\tWSAEINVAL\n");break;
case WSAENOBUFS:printf("\tWSAENOBUFS\n");break;
case WSAENOTSOCK:printf("\tWSAENOTSOCK\n");break;
default:printf("\tUnknown error\n");
}
190
return (nError);}
return (status);}
// CR: When the socket has been setup, wait here until a connection from a client is receivedint waitForClientConnection(){
int status=0;
int addrLen=sizeof(SOCKADDR_IN);
/* WAIT HERE to allow the socket to take connections */status=listen(m_serverSocket, 1); //Supported in CE!if (status == SOCKET_ERROR){
int nError = WSAGetLastError();printf("Broke after listening. WSAGetLastError returns = %d.\n",nError);switch (nError){
case WSANOTINITIALISED:printf("\tA successful WSAStartup must occur before using this function\n");break;
case WSAENETDOWN:printf("\tThe network subsystem has failed.\n");break;
case WSAEINPROGRESS:printf("\tWSAEINPROGRESS\n");break;
case WSAEINVAL:printf("\tWSAEINVAL\n");break;
case WSAEISCONN:printf("\tWSAEISCONN\n");break;
case WSAEMFILE:printf("\tWSAEMFILE\n");break;
case WSAENOBUFS:printf("\tWSAENOBUFS\n");break;
case WSAENOTSOCK:printf("\tWSAENOTSOCK\n");break;
case WSAEOPNOTSUPP:printf("\tWSAEOPNOTSUPP\n");break;
default:printf("\tUnknown error\n");
}return(status);
}
/* accept the connection request when one is received */m_clientSocket=accept(m_serverSocket,(LPSOCKADDR) &m_clientSockAddr,&addrLen); //Supported in CE!if (m_clientSocket == INVALID_SOCKET){
printf("Broke after accepting. WSAGetLastError returns = %d.\n",WSAGetLastError());return(INVALID_SOCKET);
}
return (status);}
// CR: Once a connection from a socket client has been received, read the number of bytes. The data is// read 1 byte at a time to provide a simple solution to the problem that the client can be sending the// data in different chunks which can be greater or equal to 2 bytes/chunk.
191
//int talkToClient(){
char *pBuffer=NULL;pBuffer = new char [NUM_COUNT_CHARACTERS+1];memset(pBuffer,NULL,NUM_COUNT_CHARACTERS+1);
int numrcv=0;int i=0, numOfChars=0;// Read the number of characters first (the 1st two chars of the data)for (i=0;i < NUM_COUNT_CHARACTERS; i++){
numrcv=recv(m_clientSocket,pBuffer+i,1,NO_FLAGS_SET); //Supported in CE!}sscanf(pBuffer,"%X",&numOfChars);if (pBuffer)
delete [] pBuffer;pBuffer=NULL;pBuffer = new char [numOfChars+1];memset(pBuffer,NULL,numOfChars+1);// Read the number data bytesnumrcv=0;for (i=0;i<numOfChars; i++){//Supported in CE!
numrcv+=recv(m_clientSocket,pBuffer+i,1,NO_FLAGS_SET); //CR. increment the count for every bytereceived
//*****************************************************// AcmApp.h : Declaration of the CSpeechPlayer//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// REUSE NOTE: This file contains code adapted and modified by the// author, from Microsoft's AcmApp application found in the// Developer's Network Library.//-----------------------------------------------------#include <windows.h>#include <mmsystem.h>#include <mmreg.h>#include <msacm.h>
//*****************************************************// SpeechPlayer.h : Declaration of the CSpeechPlayer//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Playback.dll workspace. Changes made by the author to the// file are marked by 'Comments by CR:'.// This is the header file for the implementation of the CSpeechPlayer// class.//// REUSE NOTE: This file contains code adapted and modified by the// author, from Microsoft's AcmApp application found in the// Developer's Network Library.//-----------------------------------------------------
// Comments by CR: This define is used to conditionally compile the section of// code that permits introducing uncompression codec into the software.//#define DEBUGGING_COMPRESSION 0
// Comments by CR: Because this coclass is created when the message is already// stored, FinalConstruct() will uncompress the file receivedHRESULT FinalConstruct(){
m_paafd = & m_aafd;
if ( this->AcmAppInit() == FALSE )return (! S_OK);
this->AcmAppPlayRecordInitCommands();
// Comments by CR: if required, uncompress the speech if it was sent with compression.// UncompressSpeech() will expect to uncompress a compressed wav file named
VOICE_FILE_PATH/VOICE_FILE_NAME,// and save it under the same name// Note: Uncompression is turned on by adding the uncompressing code, not selectable at runtime.//
//// Comments by CR: This conditionally compiled section is used to when debugging the introduction// of a X-to-PCM codec into this system. Two files are created so that the original file is// kept and can be analyzed with the Microsoft Sound /Recorder Player. When the code is debugged,// remove the DEBUGGING_COMPRESSION sections.//
sprintf(szUncompressedVoiceFilePath,"%s%s",m_szInitialDirSave,UNCOMPRESSED_VOICE_FILE_NAME);// Comments by CR: set the uncompressed voice filenamestrcpy(m_szInitialFileTitle,szUncompressedVoiceFilePath);this->UncompressSpeech(szUncompressedVoiceFilePath);
#elsechar szVoiceFilePath[256];// Comments by CR: set the uncompressed voice filenamesprintf(szVoiceFilePath,"%s%s",m_szInitialDirSave,m_szInitialFileTitle);this->UncompressSpeech(szVoiceFilePath);
#endif
201
if ( this->AppFileOpen() == FALSE )return (! S_OK);
if ( this->AcmAppPlayRecordOpen() == FALSE )return (! S_OK );
if ( this->AcmAppPlayRecordStatus() == FALSE )return (! S_OK );
//*****************************************************// stdafx.h : include file for standard system include files,// or project specific include files that are used frequently,// but are changed infrequently//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Playback.dll workspace.//-----------------------------------------------------#if !defined(AFX_STDAFX_H__EBBB8876_7B2D_11D3_841E_00C04F776696__INCLUDED_)#define AFX_STDAFX_H__EBBB8876_7B2D_11D3_841E_00C04F776696__INCLUDED_
#include <atlbase.h>//You may derive a class from CComModule and use it if you want to override//something, but do not change the name of _Moduleextern CComModule _Module;#include <atlcom.h>
//{{AFX_INSERT_LOCATION}}// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
//******************************************************// Playback.cpp : Implementation of DLL Exports.// Note: Proxy/Stub Information// To build a separate proxy/stub DLL,// run nmake -f Playbackps.mk in the project directory.//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Playback.dll workspace. No changes were made to the// automatically generated version.// Playback.dll is the COM server DLL that provides the interfaces// required to playback a PCM wav file.//-----------------------------------------------------
//*****************************************************// Playback.idl : IDL source for Playback.dll//
// This file will be processed by the MIDL tool to// produce the type library (Playback.tlb) and marshalling code.//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Playback.dll workspace. Changes made by the author to the// file are marked by 'Comments by CR:'.// Playback.dll is the COM server DLL that provides the interfaces// required to playback a PCM wav voice/sound file .// This file provides the interface definition language (idl)// for the interfaces exposed by coclasses in the Playback.dll.//-----------------------------------------------------
]// Comments by CR: The ISpeechUncompressor is required to uncompress the voice file// before it is played back. This interface is not currently implemented, but its// methods are stubbed and should be called from the COM client to permit future// compatibility.interface ISpeechUncompressor : IUnknown{
//*****************************************************// SpeechPlayer.cpp : Implementation of CSpeechPlayer//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Playback.dll workspace. Changes made by the author to the// file are marked by 'Comments by CR:'.// This is the implementation of the CSpeechPlayer class. CSpeechPlayer// the C++ dependent implementation of the Recorder coclass.//// SpeechPlayer is a COM component that permits the user to compress a// PCM WAV file, and playback.//// REUSE NOTE: This file contains code adapted and modified by the// author, from Microsoft's AcmApp application found in the// Developer's Network Library.//-----------------------------------------------------
#include "stdafx.h"#include <stdio.h> // added for min dependencies#include "Playback.h"#include "SpeechPlayer.h"
// Comments by CR: This is the method called by the COM client when the message is// ready to be played back.//STDMETHODIMP CSpeechPlayer::StartPlayback(){
// Comments by CR: Start the playbackthis->AcmAppPlayRecordPlay();this->AcmAppPlayRecordStatus();
// Comments by CR: to determine the time to playback before releasing objectdouble dTimeToPlaybackMs = (double) 1000* (m_paafd->dwDataBytes) / ((m_paafd->pwfx)->nAvgBytesPerSec);Sleep( (DWORD) dTimeToPlaybackMs );return S_OK;
}
//******************************************************************************// Comments by CR: This is convenience method provided in the interface to allow// the voice playback to be aborted.//-----------------------------------------------------------------------------STDMETHODIMP CSpeechPlayer::StopPlayback(){
if ( this->AcmAppPlayRecordStop() == FALSE )return ( !S_OK );
this->AcmAppPlayRecordStatus();
if ( this->AcmAppPlayRecordClose() == FALSE )return ( !S_OK );
m_fFileOpen = FALSE;
return S_OK;}
//******************************************************************************// Comments by CR: This is convenience method provided in the interface for future use// This method shall convert a compressed wav file named pFileName, into a PCM// wav file named pFileName//-----------------------------------------------------------------------------STDMETHODIMP CSpeechPlayer::UncompressSpeech(LPSTR pFileName){
// TODO: Add your implementation code here
return S_OK;}
//==========================================================================;//////////==========================================================================;//******************************************************************************// Comments by CR: Following is the code adapted from the Microsoft's AcmApp. A lot// of code overhead was removed (user confirmation dialogs, compatiblity with Win16,// and others). This code contains the algorithms used to access the Microsoft's// Audio Compression Manager (ACM) to: open, and manage media WAV files, via the// Media Control Interface (mci).//-----------------------------------------------------------------------------
//--------------------------------------------------------------------------;//// BOOL AppTitle//// Description:// This function formats and sets the title text of the application's
209
// window.//// Arguments:// HWND hwnd: Handle to application window to set title text for.//// PTSTR pszFileTitle: Pointer to file title to display.//// Return (BOOL):// The return value is always TRUE.//////--------------------------------------------------------------------------;
MEditPrintF(hedit, TEXT("%25s: %u"), (LPTSTR)TEXT("Bits Per Sample"), pwfx->wBitsPerSample);
if (WAVE_FORMAT_PCM != pwfx->wFormatTag) { AppFormatBigNumber(ach, pwfx->cbSize); MEditPrintF(hedit, TEXT("%25s: %s bytes\r\n"), (LPTSTR)TEXT("Extra Format Information"), (LPTSTR)ach);
AcmAppDumpExtraHeaderData(hedit, pwfx); }
// // note that we do NOT set the 'WAVE_ALLOWSYNC' bit on queries because // the player/recorder dialog uses MCIWAVE--which cannot work with // SYNC devices. // mmr = waveOutOpen(NULL, m_uWaveOutId,#if (WINVER < 0x0400) (LPWAVEFORMAT)pwfx,#else pwfx,#endif 0L, 0L, WAVE_FORMAT_QUERY);
fCanPlayRecord = (MMSYSERR_NOERROR == mmr);
if (!fCanPlayRecord) { // // this situation can happen with the 'preferred' device settings // for the Sound Mapper. // mmr = waveInOpen(NULL, m_uWaveInId,#if (WINVER < 0x0400) (LPWAVEFORMAT)pwfx,#else pwfx,#endif 0L, 0L, WAVE_FORMAT_QUERY);
// Description:// This function changes the cursor to that of the hour glass or// back to the previous cursor.//// This function can be called recursively.//// Arguments:// BOOL fHourGlass: TRUE if we need the hour glass. FALSE if we need// the arrow back.//// Return (void):// On return, the cursor will be what was requested.////--------------------------------------------------------------------------;
// // get the name for the format tag of the specified format // if (NULL != pszFormatTag) {
215
ACMFORMATTAGDETAILS aftd;
// // initialize all unused members of the ACMFORMATTAGDETAILS // structure to zero // memset(&aftd, 0, sizeof(aftd));
// // fill in the required members of the ACMFORMATTAGDETAILS // structure for the ACM_FORMATTAGDETAILSF_FORMATTAG query // aftd.cbStruct = sizeof(aftd); aftd.dwFormatTag = pwfx->wFormatTag;
// // ask the ACM to find the first available driver that // supports the specified format tag // mmr = acmFormatTagDetails(NULL, &aftd, ACM_FORMATTAGDETAILSF_FORMATTAG); if (MMSYSERR_NOERROR == mmr) { // // copy the format tag name into the caller's buffer // lstrcpy(pszFormatTag, aftd.szFormatTag); } else { PTSTR psz;
// // no ACM driver is available that supports the // specified format tag //
f = FALSE; psz = NULL;
// // the following stuff if proof that the world does NOT need // yet another ADPCM algorithm!! // switch (pwfx->wFormatTag) { case WAVE_FORMAT_UNKNOWN: psz = TEXT("** RESERVED INVALID TAG **"); break;
case WAVE_FORMAT_PCM: psz = TEXT("PCM"); break;
case WAVE_FORMAT_ADPCM: psz = TEXT("Microsoft ADPCM"); break;
case 0x0003: psz = TEXT("MV's *UNREGISTERED* ADPCM"); break;
case WAVE_FORMAT_IBM_CVSD: psz = TEXT("IBM CVSD"); break;
case WAVE_FORMAT_ALAW:
216
psz = TEXT("A-Law"); break;
case WAVE_FORMAT_MULAW: psz = TEXT("u-Law"); break;
case WAVE_FORMAT_OKI_ADPCM: psz = TEXT("OKI ADPCM"); break;
case WAVE_FORMAT_IMA_ADPCM: psz = TEXT("IMA/DVI ADPCM"); break;
case WAVE_FORMAT_DIGISTD: psz = TEXT("DIGI STD"); break;
case WAVE_FORMAT_DIGIFIX: psz = TEXT("DIGI FIX"); break;
case WAVE_FORMAT_YAMAHA_ADPCM: psz = TEXT("Yamaha ADPCM"); break;
case WAVE_FORMAT_SONARC: psz = TEXT("Sonarc"); break;
case WAVE_FORMAT_DSPGROUP_TRUESPEECH: psz = TEXT("DSP Group TrueSpeech"); break;
case WAVE_FORMAT_ECHOSC1: psz = TEXT("Echo SC1"); break;
case WAVE_FORMAT_AUDIOFILE_AF36: psz = TEXT("Audiofile AF36"); break;
case WAVE_FORMAT_CREATIVE_ADPCM: psz = TEXT("Creative Labs ADPCM"); break;
case WAVE_FORMAT_APTX: psz = TEXT("APTX"); break;
case WAVE_FORMAT_AUDIOFILE_AF10: psz = TEXT("Audiofile AF10"); break;
case WAVE_FORMAT_DOLBY_AC2: psz = TEXT("Dolby AC2"); break;
case WAVE_FORMAT_MEDIASPACE_ADPCM: psz = TEXT("Media Space ADPCM"); break;
case WAVE_FORMAT_SIERRA_ADPCM: psz = TEXT("Sierra ADPCM"); break;
case WAVE_FORMAT_G723_ADPCM:
217
psz = TEXT("CCITT G.723 ADPCM"); break;
case WAVE_FORMAT_GSM610: psz = TEXT("GSM 6.10"); break;
case WAVE_FORMAT_G721_ADPCM: psz = TEXT("CCITT G.721 ADPCM"); break;
case WAVE_FORMAT_DEVELOPMENT: psz = TEXT("** RESERVED DEVELOPMENT ONLY TAG **"); break;
if (NULL != psz) { lstrcpy(pszFormatTag, psz); } } }
// // get the description of the attributes for the specified // format // if (NULL != pszFormat) { ACMFORMATDETAILS afd;
// // initialize all unused members of the ACMFORMATDETAILS // structure to zero // memset(&afd, 0, sizeof(afd));
// // fill in the required members of the ACMFORMATDETAILS // structure for the ACM_FORMATDETAILSF_FORMAT query // afd.cbStruct = sizeof(afd); afd.dwFormatTag = pwfx->wFormatTag; afd.pwfx = pwfx;
// // the cbwfx member must be initialized to the total size // in bytes needed for the specified format. for a PCM // format, the cbSize member of the WAVEFORMATEX structure // is not valid. // if (WAVE_FORMAT_PCM == pwfx->wFormatTag) { afd.cbwfx = sizeof(PCMWAVEFORMAT); } else { afd.cbwfx = sizeof(WAVEFORMATEX) + pwfx->cbSize; }
// // ask the ACM to find the first available driver that // supports the specified format //
218
mmr = acmFormatDetails(NULL, &afd, ACM_FORMATDETAILSF_FORMAT); if (MMSYSERR_NOERROR == mmr) { // // copy the format attributes description into the caller's // buffer // lstrcpy(pszFormat, afd.szFormat); } else { TCHAR ach[2]; TCHAR szChannels[24]; UINT cBits;
// // no ACM driver is available that supports the // specified format //
f = FALSE;
// // // ach[0] = gchIntlList; ach[1] = '\0';
gchIntlList = ach[0];
ach[0] = gchIntlDecimal; ach[1] = '\0';
gchIntlDecimal = ach[0];
// // compute the bit depth--this _should_ be the same as // wBitsPerSample, but isn't always... // cBits = (UINT)(pwfx->nAvgBytesPerSec * 8 / pwfx->nSamplesPerSec / pwfx->nChannels);
if ((1 == pwfx->nChannels) || (2 == pwfx->nChannels)) { if (1 == pwfx->nChannels) lstrcpy(szChannels, TEXT("Mono")); else lstrcpy(szChannels, TEXT("Stereo"));
// Comments by CR: Believe it or not, THIS IS Microsoft code (+ comments) !!! // // !!! this is really horrible code !!! //{ #define ACMAPP_DUMP_BYTES_PER_LINE 16
UINT u; UINT v;
for (u = 0; u < pwfx->cbSize; u += ACMAPP_DUMP_BYTES_PER_LINE) { MEditPrintF(hedit, TEXT("~0x%.04X"), u);
for (v = 0; v < ACMAPP_DUMP_BYTES_PER_LINE; v++) { if ((u + v) >= pwfx->cbSize) break;
//--------------------------------------------------------------------------;//// int MEditPrintF//// Description:// This function is used to print formatted text into a Multiline// Edit Control as if it were a standard console display. This is// a very easy way to display small amounts of text information// that can be scrolled and copied to the clip-board.//// Arguments:// HWND hedit: Handle to a Multiline Edit control.//// PTSTR pszFormat: Pointer to any valid format for wsprintf. If// this argument is NULL, then the Multiline Edit Control is cleared// of all text.////// Return (int):// Returns the number of characters written into the edit control.//// Notes:// The pszFormat string can contain combinations of escapes that// modify the default behaviour of this function. Escapes are single// character codes placed at the _beginning_ of the format string.//// Current escapes defined are://// ~ : Suppresses the default CR/LF added to the end of the// printed line. Since the most common use of this function// is to output a whole line of text with a CR/LF, that is// the default.//// ` : Suppresses logging to the debug terminal (regardless of// the global debug log options flag).//////--------------------------------------------------------------------------;
// // if the pszFormat argument is NULl. // if (NULL == pszFormat) { AcmAppDebugLog(NULL);
return (0); }
// // format and display the string in the window... first search for // escapes to modify default behaviour. // for (;;) { switch (*pszFormat) { case '~': fCRLF = FALSE; pszFormat++; continue;
case '`': fDebugLog = FALSE; pszFormat++; continue; }
break; }
va_start(va, pszFormat);#ifdef WIN32 n = wvsprintf(ach, pszFormat, va);#else n = wvsprintf(ach, pszFormat, (LPSTR)va);#endif va_end(va);
if (fDebugLog) { AcmAppDebugLog(ach); }
if (fCRLF) { if (fDebugLog) { AcmAppDebugLog(szCRLF); } }
return (n);} // MEditPrintF()
//--------------------------------------------------------------------------;//// void AcmAppDebugLog//// Description:// This function logs information to the debugger if the Debug Log// option is set. You can then run DBWin (or something similar)// to redirect the output whereever you want. Very useful for debugging// ACM drivers.//
222
// Arguments:// PTSTR pszFormat: Pointer to any valid format for wsprintf.//// Return (void):// None.////--------------------------------------------------------------------------;
// // format and display the string in a message box... // va_start(va, pszFormat);#ifdef WIN32 wvsprintf(ach, pszFormat, va);#else wvsprintf(ach, pszFormat, (LPSTR)va);#endif va_end(va);
OutputDebugString(ach); }} // AcmAppDebugLog()
//--------------------------------------------------------------------------;//// BOOL AcmAppFileNew//// Description://// Arguments:// HWND hwnd: Handle to main window.//// PACMAPPFILEDESC m_paafd: Pointer to file descriptor.//// Return (BOOL):////--------------------------------------------------------------------------;
// // create the RIFF chunk of form type 'WAVE' // ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E'); ckRIFF.cksize = 0L; mmioCreateChunk(hmmio, &ckRIFF, MMIO_CREATERIFF);
// // now create the destination fmt, fact, and data chunks _in that order_ // // hmmio is now descended into the 'RIFF' chunk--create the format chunk // and write the format header into it // cbwfx = SIZEOF_WAVEFORMATEX(pwfx);
// // create the 'fact' chunk (not necessary for PCM--but is nice to have) // since we are not writing any data to this file (yet), we set the // samples contained in the file to 0.. // ck.ckid = mmioFOURCC('f', 'a', 'c', 't'); ck.cksize = 0L; mmioCreateChunk(hmmio, &ck, 0);
//--------------------------------------------------------------------------;//// BOOL AcmAppFileOpen//// Description:// This function opens the specified file and get the important info// from it.//// NOTE! This function does NOT check for a modified file! It is// assumed that the calling function took care of everything before// calling this function.//// Arguments:// HWND hwnd: Handle to main window.//// PACMAPPFILEDESC m_paafd: Pointer to file descriptor.//// Return (BOOL):// The return value is TRUE if the function is successful. It is FALSE// if an error occurred. If an error does occur, then the contents// of the file descriptor will remain unchanged.////
//--------------------------------------------------------------------------;//// BOOL AppGetFileTitle//// Description:// This function extracts the file title from a file path and returns// it in the caller's specified buffer.//// Arguments:// PTSTR pszFilePath: Pointer to null terminated file path.//// PTSTR pszFileTitle: Pointer to buffer to receive the file title.//// Return (BOOL):// Always returns TRUE. But should return FALSE if this function// checked for bogus values, etc.//////--------------------------------------------------------------------------;
// scan to the end of the file path string.. // for (pch = pszFilePath; '\0' != *pch; pch++) ;
// // now scan back toward the beginning of the string until a slash (\), // colon, or start of the string is encountered. // while ((pch >= pszFilePath) && !IS_SLASH(*pch) && (':' != *pch)) { pch--; }
// // finally, copy the 'title' into the destination buffer.. skip ahead // one char since the above loop steps back one too many chars... // lstrcpy(pszFileTitle, ++pch);
// // first try to open the file, etc.. open the given file for reading // using buffered I/O // hmmio = mmioOpen((LPTSTR)pszFilePath, NULL, MMIO_READ | MMIO_ALLOCBUF); if (NULL == hmmio) goto wio_Open_Error;
pwio->hmmio = hmmio;
// // locate a 'WAVE' form type... // ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E'); if (mmioDescend(hmmio, &ckRIFF, NULL, MMIO_FINDRIFF)) goto wio_Open_Error;
// // we found a WAVE chunk--now go through and get all subchunks that // we know how to deal with... // pwio->dwDataSamples = (DWORD)-1L;
#if 0 if (lrt=riffInitINFO(&wio.pInfo)) { lr=lrt; goto wio_Open_Error; }#endif
// // // while (MMSYSERR_NOERROR == mmioDescend(hmmio, &ck, &ckRIFF, 0)) { // // quickly check for corrupt RIFF file--don't ascend past end! // if ((ck.dwDataOffset + ck.cksize) > (ckRIFF.dwDataOffset + ckRIFF.cksize)) {
// Comments by CR: Believe it or not, THIS IS Microsoft's code !!! werr = WIOERR_BADFILE; goto wio_Open_Error; }
case mmioFOURCC('D', 'I', 'S', 'P'):#if 0 riffReadDISP(hmmio, &ck, &(wio.pDisp));#endif break;
230
case mmioFOURCC('f', 'm', 't', ' '): // // !?! another format chunk !?! // if (NULL != pwio->pwfx) break;
// // get size of the format chunk, allocate and lock memory // for it. we always alloc a complete extended format header // (even for PCM headers that do not have the cbSize field // defined--we just set it to zero). // dw = ck.cksize; if (dw < sizeof(WAVEFORMATEX)) dw = sizeof(WAVEFORMATEX);
pwio->pwfx = (LPWAVEFORMATEX) new WAVEFORMATEX[dw]; if (NULL == pwio->pwfx) { werr = WIOERR_NOMEM; goto wio_Open_Error; }
// // read the format chunk // werr = WIOERR_FILEERROR; dw = ck.cksize; if (mmioRead(hmmio, (HPSTR)pwio->pwfx, dw) != (LONG)dw) goto wio_Open_Error; break;
case mmioFOURCC('d', 'a', 't', 'a'): // // !?! multiple data chunks !?! // if (0L != pwio->dwDataBytes) break;
// // just hang on to the total length in bytes of this data // chunk.. and the offset to the start of the data // pwio->dwDataBytes = ck.cksize; pwio->dwDataOffset = ck.dwDataOffset; break;
case mmioFOURCC('f', 'a', 'c', 't'): // // !?! multiple fact chunks !?! // if (-1L != pwio->dwDataSamples) break;
// // read the first dword in the fact chunk--it's the only // info we need (and is currently the only info defined for // the fact chunk...) // // if this fails, dwDataSamples will remain -1 so we will // deal with it later... // mmioRead(hmmio, (HPSTR)&pwio->dwDataSamples, sizeof(DWORD)); break;
231
}
// // step up to prepare for next chunk.. // mmioAscend(hmmio, &ck, 0); }
// // if no fmt chunk was found, then die! // if (NULL == pwio->pwfx) { werr = WIOERR_ERROR; goto wio_Open_Error; }
// // all wave files other than PCM are _REQUIRED_ to have a fact chunk // telling the number of samples that are contained in the file. it // is optional for PCM (and if not present, we compute it here). // // if the file is not PCM and the fact chunk is not found, then fail! // if (-1L == pwio->dwDataSamples) { if (WAVE_FORMAT_PCM == pwio->pwfx->wFormatTag) { pwio->dwDataSamples = pwio->dwDataBytes / pwio->pwfx->nBlockAlign; } else {
// Comments by CR: Believe it or not, THIS IS Microsoft's code (+ comments) !!! // // !!! HACK HACK HACK !!! // // although this should be considered an invalid wave file, we // will bring up a message box describing the error--hopefully // people will start realizing that something is missing??? // werr = WIOERR_BADFILE; goto wio_Open_Error;
// // !!! need to hack stuff in here !!! // pwio->dwDataSamples = 0L; } }
// // cool! no problems.. // return (WIOERR_NOERROR);
// // return error (after minor cleanup) //wio_Open_Error:
WIOERR CSpeechPlayer:: wioFileClose( LPWAVEIOCB pwio, DWORD fdwClose){ // // validate a couple of things... // if (NULL == pwio) return (WIOERR_BADPARAM);
// // get rid of stuff... // if (NULL != pwio->hmmio) { mmioClose(pwio->hmmio, 0); }
#if 0 if (pwio->pInfo) riffFreeINFO(&(lpwio->pInfo));
if (pwio->pDisp) riffFreeDISP(&(lpwio->pDisp));#endif
return (WIOERR_NOERROR);} // wioFileClose()
//--------------------------------------------------------------------------;//// BOOL AppFileNew//// Description:// This function is called to handle the IDM_FILE_NEW message. It is// responsible for clearing the working area for a new unnamed file.//// Arguments:// HWND hwnd: Handle to application window.//// PACMAPPFILEDESC m_paafd: Pointer to current file descriptor.//// Return (BOOL):// The return value is TRUE if the working area was cleared and is// ready for new stuff. The return value is FALSE if the user canceled// the operation.////--------------------------------------------------------------------------;
BOOL CSpeechPlayer:: AppFileNew( BOOL fCreate
233
){ BOOL f;
if (fCreate) { f = AcmAppFileNew();//*CR. if (!f) return (FALSE); } else { // // if there is currently a file path, then we have to do some real // work... // if ('\0' != m_paafd->szFilePath[0]) {
f = AcmAppFileNew();//*CR. if (!f) return (FALSE); }
// // blow away the old file path and title; set the window title // and return success // lstrcpy(m_paafd->szFilePath, m_szFileUntitled); lstrcpy(m_paafd->szFileTitle, m_szFileUntitled); }
AppTitle(m_paafd->szFileTitle);
return (TRUE);} // AppFileNew()
//--------------------------------------------------------------------------;//// BOOL AppFileOpen//// Description:// This function handles the IDM_FILE_OPEN message. It is responsible// for getting a new file name from the user and opening that file// if possible.//// Arguments:// HWND hwnd: Handle to application window.//// PACMAPPFILEDESC m_paafd: Pointer to current file descriptor.//// Return (BOOL):// The return value is TRUE if a new file was selected and opened.// It is FALSE if the user canceled the operation.////--------------------------------------------------------------------------;
lstrcpy(m_paafd->szFilePath, szFilePath); lstrcpy(m_paafd->szFileTitle, szFileTitle); f = AcmAppFileOpen();//*CR. if ( f ) { // // set the window title text... // AppTitle(szFileTitle);//*CR. AcmAppDisplayFileProperties();//*CR. }
//--------------------------------------------------------------------------;//// MCIERROR AcmPlayRecordSendCommand//// Description:// The string is of the form "verb params" our device name is inserted// after the verb and send to the device.//// Arguments:// HWND hwnd://// PSTR pszCommand://// PSTR pszReturn://// UINT cbReturn://// BOOL fErrorBox://// Return (MCIERROR)://////--------------------------------------------------------------------------;
TEXT(""), TEXT("record insert !"), TEXT("record overwrite !"), TEXT("record to Y !"), TEXT("record from X to Y !"),
TEXT(""), TEXT("resume"),
TEXT(""), TEXT("save"), TEXT("save FILENAME"),
TEXT(""), TEXT("seek to Y"), TEXT("seek to start"), TEXT("seek to end"),
TEXT(""), TEXT("set alignment X"), TEXT("set any input"), TEXT("set any output"), TEXT("set audio all off"), TEXT("set audio all on"), TEXT("set audio left off"), TEXT("set audio left on"), TEXT("set audio right off"), TEXT("set audio right on"), TEXT("set bitspersample X"), TEXT("set bytespersec X"), TEXT("set channels X"), TEXT("set format tag X"), TEXT("set format tag pcm"), TEXT("set input X"), TEXT("set output X"), TEXT("set samplespersec X"), TEXT("set time format bytes"), TEXT("set time format milliseconds"), TEXT("set time format samples"),
TEXT(""), TEXT("status alignment"), TEXT("status bitspersample"), TEXT("status bytespersec"), TEXT("status channels"), TEXT("status current track"), TEXT("status format tag"), TEXT("status input"), TEXT("status length"), TEXT("status length track X"), TEXT("status level"), TEXT("status media present"), TEXT("status mode"), TEXT("status number of tracks"), TEXT("status output"), TEXT("status position"), TEXT("status position track X"), TEXT("status ready"), TEXT("status samplespersec"),
242
TEXT("status start position"), TEXT("status time format"),
//*****************************************************// stdafx.cpp : source file that includes just the standard includes// stdafx.pch will be the pre-compiled header// stdafx.obj will contain the pre-compiled type information//-----------------------------------------------------// Wireless Instant Voice Messaging Over the Internet// A Master of Science Thesis Presented to the Electtrical// and Computer Engineering Department of the University of// Florida, Gainesville, FL USA.//// by : Carlos A. Rivera-Cintron// Date: February 10, 2000//// This file was automatically generated by the// Microsoft Visual C++ 6.0 program, after the author created// the Playback.dll workspace.//-----------------------------------------------------#include "stdafx.h"
Reply from 209.214.13.8: bytes=32 time=1547ms TTL=115Reply from 209.214.13.8: bytes=32 time=1398ms TTL=115Reply from 209.214.13.8: bytes=32 time=1100ms TTL=115Reply from 209.214.13.8: bytes=32 time=1610ms TTL=115Reply from 209.214.13.8: bytes=32 time=1585ms TTL=115Reply from 209.214.13.8: bytes=32 time=1990ms TTL=115Reply from 209.214.13.8: bytes=32 time=1745ms TTL=115Reply from 209.214.13.8: bytes=32 time=1490ms TTL=115Reply from 209.214.13.8: bytes=32 time=1700ms TTL=115Reply from 209.214.13.8: bytes=32 time=1700ms TTL=115Reply from 209.214.13.8: bytes=32 time=1420ms TTL=115Reply from 209.214.13.8: bytes=32 time=1385ms TTL=115Reply from 209.214.13.8: bytes=32 time=1590ms TTL=115Reply from 209.214.13.8: bytes=32 time=1805ms TTL=115Reply from 209.214.13.8: bytes=32 time=1615ms TTL=115Reply from 209.214.13.8: bytes=32 time=1495ms TTL=115Reply from 209.214.13.8: bytes=32 time=1500ms TTL=115Reply from 209.214.13.8: bytes=32 time=1305ms TTL=115Reply from 209.214.13.8: bytes=32 time=995ms TTL=115Reply from 209.214.13.8: bytes=32 time=1400ms TTL=115Reply from 209.214.13.8: bytes=32 time=1105ms TTL=115Reply from 209.214.13.8: bytes=32 time=1905ms TTL=115Reply from 209.214.13.8: bytes=32 time=1839ms TTL=115Reply from 209.214.13.8: bytes=32 time=2450ms TTL=115Request timed out.Reply from 209.214.13.8: bytes=32 time=1678ms TTL=115Reply from 209.214.13.8: bytes=32 time=1795ms TTL=115Reply from 209.214.13.8: bytes=32 time=1595ms TTL=115Reply from 209.214.13.8: bytes=32 time=1605ms TTL=115Reply from 209.214.13.8: bytes=32 time=1400ms TTL=115Reply from 209.214.13.8: bytes=32 time=1405ms TTL=115Reply from 209.214.13.8: bytes=32 time=1600ms TTL=115Reply from 209.214.13.8: bytes=32 time=1600ms TTL=115Reply from 209.214.13.8: bytes=32 time=1805ms TTL=115Reply from 209.214.13.8: bytes=32 time=1205ms TTL=115Reply from 209.214.13.8: bytes=32 time=1400ms TTL=115Reply from 209.214.13.8: bytes=32 time=1499ms TTL=115Reply from 209.214.13.8: bytes=32 time=1605ms TTL=115Reply from 209.214.13.8: bytes=32 time=1835ms TTL=115Reply from 209.214.13.8: bytes=32 time=1670ms TTL=115Reply from 209.214.13.8: bytes=32 time=1515ms TTL=115Reply from 209.214.13.8: bytes=32 time=1890ms TTL=115Reply from 209.214.13.8: bytes=32 time=1400ms TTL=115Reply from 209.214.13.8: bytes=32 time=1750ms TTL=115Reply from 209.214.13.8: bytes=32 time=1760ms TTL=115Reply from 209.214.13.8: bytes=32 time=1600ms TTL=115Request timed out.Reply from 209.214.13.8: bytes=32 time=1348ms TTL=115
246
Reply from 209.214.13.8: bytes=32 time=1694ms TTL=115Reply from 209.214.13.8: bytes=32 time=1600ms TTL=115
Ping statistics for 209.214.13.8: Packets: Sent = 50, Received = 48, Lost = 2 (4% loss),Approximate round trip times in milli-seconds: Minimum = 995ms, Maximum = 2450ms, Average = 1518ms
C.2 Boynton (client) Boynton (server), 50kB file transfer using 576 byte buffer
[THIS IS THE PACKET WITH THE SIX BYTES SPECIFYING THE SIZE OF THE VOICE FILE]22:45:52.669814 44:45:53:54:0:0 20:53:52:43:0:0 0800 60: 166.62.191.207.1190 > 216.78.85.19.6300: P 1:7(6) ack 1 win 8576(DF)
[THIS IS THE PACKET THAT BEGINS THE TRANSFER OF THE 25K FILE]22:45:52.680175 44:45:53:54:0:0 20:53:52:43:0:0 0800 590: 166.62.191.207.1190 > 216.78.85.19.6300: P 7:543(536) ack 1 win8576 (DF)
[1] Hon, A.S., An Introduction To Paging - What It Is And How It Works,http://www.mot.com/MIMS/MSPG/Special/explain_paging/ptoc.html , MotorolaElectronics Pte Ltd., 1996.
[4] Motorola, PageWriter™ 2000 Service Manual, 1997.
[5] Couch, Leon W., Digital and Analog Communications Systems, 3rd Edition,Macmillan, New York, 1990.
[6] Sanchez, Carlos, Multimedia Services over Wireless Cellular Networks, Master’sThesis to the University of Florida, 2000.
[7] Bertsekas, Dimitri and Robert Gallager, Data Networks, 2nd Edition, Prentice Hall,Upper Saddle River, NJ, 1992.
[8] Microsoft, Microsoft TechNet, http://technet.microsoft.com/cdonline/content/, 1999.
[9] Larkin, Joseph C., Packet Telephony - Implementing a Testbed, Master’s Thesis to theUniversity of Florida, 1996.
[10] Goncalves, Marcus, Voice Over IP Networks, McGraw-Hill, New York, 1999.
[11] Cattermole, KW, “Pulse Code Modulation: Invented for Microwaves, UsedEverywhere,” 100 Years of Radio, IEE, University of Essex, United Kingdom, 1995.
[12] Roy, Tirtha, Voice Over IP - Implementing a Packet Telehony Gateway Testbed,Master’s Thesis to the University of Florida, 1998.
[13] Kuruppillai, Rajan and Mahi Dontamsetti and Fil J. Consentino, Wireless PCS,McGraw-Hill, New York, 1996.
[14] Charoenruengkit, Werayuth, The Speech Coding Based on the GELP Algorithm,Master’s Thesis to the University of Florida, 1999.
[15] Childers, DG and C-F Wong, “Measuring and Modeling Vocal Source-TractInteraction,” IEEE Transactions on Biomedical Engineering, Volume 41, Issue 7, July1994.
[16] McNealis, Martin, IP Crossroads ,http://www.cisco.com/warp/public/784/packet/july98/9.html , Cisco Systems, 1999.
[17] Latchman, Haniph A., Computer Communication Networks and the Internet,McGraw-Hill, New York, 1997.
[18] Deering, S. and R. Hinden, Internet Protocol Version 6 (IPv6), ftp://ftp.isi.edu/in-notes/rfc2460.txt, 1998.
[19] Eyre, J. and J. Bier, “DSPs court the consumer”, IEEE Spectrum, March 1999.
[20] Greenstreet, Debbie and Bill Witowsky, “Quality of Service in Voice-Over PacketProducts,” Multimedia Systems Design Magazine, November 1998.
[21] Microsoft, “The Component Object Model,” Microsoft Visual C++ 6.0 DevelopersNetwork Library (MSDN), 1999.
[22] Richard, Dr. Grimes and Alex Stockton and Julian Templeman and George V.Reilly, Beginning ATL COM, Wrox Press, Birmingham, UK, 1998.
[23] Rector, Brent and Chris Sells and Jim Springfield, ATL Internals, Addison-Wesley,Reading, Massachusetts, 1999.
[24] Rumbaugh, James and Michael Blaha and William Premerlani and Frederick Eddyand William Lorensen, Object Oriented Modeling and Design, Prentice Hall, UpperSaddle River, New Jersey, 1994.
[25] Perkins, C., IP Mobility Support, IETF RFC 2002, October 1996.
[26] Solomon, James, Mobile IP-The Internet Unplugged, Prentice Hall, Upper SaddleRiver, New Jersey, 1998.
[27] Simpson, W., The Point-to-Point Protocol (PPP), IETF RFC 1661, July 1994.
[28] Loyd, B. and W. Simpson, PPP Authentication Protocols, IETF RFC 1334, October1992.