Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Mobile

Building an Internet Global Phone


SP 94: Building an Internet Global Phone

Building an Internet Global Phone

Two-way voice over the Internet

Sing Li

Sing, a products architect with microWonders in Toronto, specializes in GUI portability, UNIX systems programming, digital-convergence technologies, and device drivers. You can contact him on CompuServe at 70214,3466.


The Internet connects millions of computers worldwide for the exchange of information. For the most part, this information is text data, typically in the form of e-mail. However, digitized data such as voice, photos, and video can also be exchanged over the same medium, although bandwidth limitations, the lack of global standards, and the scarcity of appropriate software has generally stood in the way of making this possible.

Voice communication in particular is an important key to achieving a true global information highway. While English is the primary language used to exchange information over the Internet, it is not the first language of millions of Internet users. Of course, hardware and software systems for translating Arabic, Far Eastern languages, and others can be built, but doing so is currently prohibitively expensive. Direct voice communications can overcome this barrier.

In this article, I'll present the "Internet Global Phone" (IGP), an application which enables two-way voice communication across the Internet. Although Windows hosted, IGP is compatible with similar utilities available for UNIX workstations. IGP, written in Visual C++ 1.5 using the Microsoft Foundation Classes (MFC) 2.5, is implemented via Windows Socket 1.1 and allows LAN communications over most TCP/IP stacks including Windows for Workgroups, Novell LAN workplace, Windows NT, and the like. In doing so, it provides real-time, on-the-fly voice compression on 80486 processors, providing serviceable voice communications over SLIP or PPP links at speeds as low as 14,400 bps. Still, with its reasonably low bandwidth requirements, IGP is a good Internet citizen and not a resource hog.

IGP Design and Implementation

A number of widely circulated voice applications are available for UNIX workstations, most of which are implemented using Berkeley Sockets. All follow similar design architectures; see Figure 1.

Defining the problem in terms of client and server components greatly simplifies the design and coding tasks. Example 1(a) is the generic pseudocode for a client, while Example 1(b) is the generic pseudocode for the server.

Coding falls out directly from the design, which relies heavily on access to built-in hardware DSP, the high processing speed of most UNIX workstations, the preemptive multitasking nature of UNIX, and the ease of use of Berkeley Sockets. In short, it is a simplistic, yet functional, design.

I began coding IGP with Visual Basic. Using an assortment of VBX components, voice input is digitized through the high-level Media Control Interface (MCI), then sent through Windows Sockets to the destination. The destination receives the data through Windows Sockets and then plays it back via MCI.

I encountered a couple of problems with this approach. For one thing, the huge size of voice files created by MCI clogged up all available bandwidth over slow links. I solved this problem by incorporating compression into the software. A second problem involved synchronization when receiving data while at the same time recording/sending information. It quickly became obvious that the serial procedure of recording-compression-transmission took too much time. Even with real-time compression algorithms, it took more than three times the recording duration before the voice was received at the other end. Figure 2 describes these time delays. CPU time hogging also occurred during the compression process, which ultimately led to the user interface being unusable for various periods.

Because of these low-level problems, I made a number of implementation changes. For one thing, I switched the development platform from Visual Basic to C/C++. Next, I began calling low-level audio routines instead of using the MCI. Finally, I directly programmed the Windows Sockets instead of using VBXs. In short, the event-driven, non-preemptive nature of Windows precluded the use of the elegantly simple UNIX-based design. (Building the implementation in Windows is more akin to embedded-systems programming than to designing high-level applications.)

All user code execution within Windows applications is in response to specific operating-system events or messages. Since Windows is not a preemptive multitasking system, servicing the messages holds up the entire Windows session. This is analogous to handling hardware interrupts with all interrupts disabled. If the application does not have other real-time messages to handle, the occasional hogging of the CPU may not be a major problem. Unfortunately in IGP, there are plenty of real-time events to handle.

IGP needs to be able to receive messages while recording, during compression, and while users are running other applications (background ftp, for instance). This means that IGP must do as little as possible between processing messages. Protocol management and audio-compression algorithms expressed in a linear fashion must be segmented to get incremental work done between successive messages.

Finally, the Windows cooperative multitasker does not keep state information for logically different threads of execution. Therefore, it is necessary for the application to carry state information for each of the logical recording buffers: management, compression, protocol management, decompression, and playback/buffer-management threads.

Conventional wisdom says that any naturally multithreaded application can be coded within a single thread/task via a huge switch mechanism and some real ugly coding. For the sake of implementability and efficiency, IGT is programmed in this less-than-elegant fashion.

Real-Time Voice Handling

Using the low-level audio routines in Windows involves the calls in Table 1. The application allocates buffers of memory (via GlobalAlloc), initializes them (via waveInPrepareHeader), and feeds them to the waveIn digitizer device (via waveInAddBuffer). As digitized audio becomes available after a waveInStart command, the system sends a message (MM_WIM_DATA) to a specified window with the currently filled buffer of audio. The application makes sure that the digitizer has additional memory buffers to work with. If buffers are not supplied to the digitizer in time, there will be dropouts in the resulting audio capture. The application also has the responsibility of deinitializing (waveInUnprepareHeader) and freeing all allocated memory buffers.

Programming the audio-playback hardware is similar to programming audio input. Buffers are allocated (via GlobalAlloc), filled with audio data, initialized (via waveOutPrepareHeader), and sent to the playback hardware (waveOut device) via the waveOutWrite call. When the system is finished playing back a buffer, a message (MM_WOM_DONE) is sent to the application-specified window that used the audio-data block. The application must then deinitialize the buffer (waveOutUnprepareHeader) and free the memory.

There is one caveat: Although most audio digitization and playback takes place via the DMA/interrupt mechanism of the audio hardware, some audio drivers still choke the playback if too much foreground time is consumed. Therefore, take care not to work the Windows message pump too hard during audio-playback periods.

In Windows 3.1, the only currently documented and universally supported audio format is WAVE_FORMAT_PCM. This format is somewhat of an anomaly in that 8- and 16-bit samples are stored inconsistently. The 8-bit data is an unsigned linear sample, and the 16-bit data is a signed sample; see Figure 3. Consequently, you can compile the IGP source with either the HI_FIDELITY switch set for 16-bit sound cards (#define HI_FIDELITY), or comment off the #define for 8-bit sound cards.

Ideally, the voice-compression algorithm should be able to execute in real time. This will allow on-the-fly compression and minimize the delay between recording and actual playback at the receiving end.

Standard waveform-coder (compression) technologies such as adaptive differential pulse code modulation (ADPCM) have a very high bit rate and very high-speed link requirements. Using mathematical models to simulate the human-voice production system, technologies based on voice coders/decoders (vocoders) can provide acceptable speech at reasonable bit-stream rates. However, vocoders are usually very computationally intensive.

Most available vocoder algorithms are designed for DSP-based implementations rather than for general-purpose processors. However, Jutta Degener and Carsten Bormann of TU Berlin have developed a 32-bit C implementation of the European GSM 0.6.10 speech-compression standard (prI-ETS 300 036), which is what IGP uses. Consequently, compression on a 33/486 CPU is very close to real time. Windows makes it necessary to recompile the algorithms in 16 bits. The current coding has some artifacts due to round-off. The resulting sound is audible and satisfactory for our purposes.

The actual GSM algorithm compresses blocks of 160 samples (13 bits) at 8-kHz sampling into 260 bits. Degener and Bormann's implementation takes 160 16-bit, linear 8-kHz samples and compresses them into a 33-byte frame, generating a bit-stream rate of 13 Kbits per second after compression. This bit rate is close to the raw bit throughput of 14,400 modems. It will be well within the capability of the new crop of V.34(V.Fast) modems operating at 28,800 baud. Using GSM will allow even slow-link (modem-connect) users of the Internet to participate in IGP activities.

The Protocol

A protocol is necessary to coordinate the transmission and receipt of voice and status information. For IGP, I selected a protocol compatible with current UNIX utilities, allowing PC users to talk to the large community of UNIX users on the Internet.

This protocol is simple and extensible, as Figure 4 illustrates. Both sides take turns being the initiator and receiver. Note that the initiator and receiver are totally independent of the role of client and server. This can be confusing since the different roles are all played by separate, logical threads of the same process or task.

Table 2 shows the logical threads of operation on a higher level. The model is for all threads to perform their tasks concurrently. During actual implementation, however, there are obvious difficulties in realizing the model fully. The half-duplex nature of Windows audio drivers will not allow audio playback during recording. More than one active server thread causes trouble, since there is no means of simultaneously opening or mixing multiple channels of digital audio output.

The Winsock Reality

Handling TCP/IP-based client/server configurations on a UNIX system involves the execution of a daemon (background) tcpd process. This "listening manager" process listens at various well-known ports for network-client connections. Once a connection is detected, tcpd consults a services system-configuration file for the actual server to invoke. It then spawns the necessary server process and connects the client-server pair with a new socket for communications. A single tcpd started during system boot will handle tcp connections for all available system servers.

Naturally, this straightforward, elegant solution has no parallel in the Windows world. No system-level support for TCP/IP-based client/server connections exists. So you have to emulate this behavior using application-level coding.

The equivalent of Berkeley Sockets on Microsoft Windows is Windows Sockets 1.1, a binary-compatible API designed to support TCP/IP protocol stacks from a variety of vendors. (See "Untangling the Windows Sockets API," by Mike Calbaum, Frank Porcaro, Mark Ruegsegger, and Bruce Backman, DDJ, February 1993.) While it is a workable standard, programming with the current Winsock 1.1 specifications is not without pain. Due to the underlying operating-system architecture and the need to be compatible with a variety of TCP/IP tools, the functions defined in Windows Sockets 1.1 are minimally adequate. Performing even the most common data transmit or receive requires a substantial amount of coding.

Until Windows Sockets 2.0 arrives on the scene or Microsoft provides system-level extensions, Mark Clouden's WSNETWRK library substantially eases the pain of programming raw Winsock by providing a higher-level abstraction of the socket paradigm. This implementation of IGP uses WSNETWRK, placing a C++ wrapper around the library, thereby providing the much-needed virtual functions to events mapping for all relevant messages to IGP. The resulting class hierarchy is much cleaner and considerably more maintainable. (For more on WSNETWRK, see the text box entitled, "The WSNETWRK Library.")

Doing it with Classes

To achieve some extensibility, it is a good idea to organize Winsock coding in a rational manner. C/C++ and MFC make the job somewhat easier. By carefully mapping system events and messages to C++ virtual functions, you can make the coding understandable. Some commercial libraries, notably MFC, use a macro-based hash table instead of C++ virtual functions to map messages. The construct preserves the clean nature of the virtual-function concept and greatly improves execution speed. I'll avoid the awkward construct since the events and messages that IGP deals with occur infrequently compared to the core Windows messages that MFC deals with. The overhead of a formal virtual function call is negligible in this case.

The new model is based on a virtual CSocketOwner class; see Figure 5. As a virtual class, no object of CSocketOwner can be instantiated. This class is derived from CObject and is designated as the base class of any object which may contain and/or own a socket. CSockOwner contains member functions that have an almost one-to-one mapping to all required functions in Winsock 1.1. It also contains the default implementation of the Notify and Callback functions. These functions intercept the messages from WSNETWRK and map them into corresponding virtual functions OnConnected, OnSendCompleted, OnReceiveCompleted, OnTimerExpired, and OnDisconnected.

From CSocketOwner, you derive three instantiable classes, CSockListenServer, CSockServer, and CSockClient. CSockListenServer, or a class based on it, has the basic functionality of tcpd in UNIX and can be asked to monitor at a certain port. Once a client connection is received, CSockListenServer will dynamically create a specific CSockServer object and create a new socket for the client/server pair. After this, CSockListenServer returns to monitor the port until it is explicitly terminated.

The CSockServer class contains the common data structures and handles the common functions of a socket server, independent of protocol. The CSockClient class does the same for socket clients.

Any protocol-specific handling is encapsulated in C<protocol specific>ListenServer, C<protocol specific>Server, and C<protocol specific>Client classes. This normalization, however, has not yet been implemented in IGP. IGP's talksock.cpp and talksock.h modules contain CTalkSockListenServer, which is derived from CSockListenServer. Both the CTalkSockServer and CTalkSockClient classes are directly derived from CSocketOwner. Propagating common functions and data up to the CSockServer and CSockClient classes makes the hierarchy reusable for new protocol implementations.

IGP Source Code

IGP is developed using Microsoft Visual C++ 1.5. It makes use of Microsoft Foundation Classes (MFC 2.5) to generate the application framework. The three source files--mainframe.cpp (Listing One), phonedoc.cpp (Listing Two), and mphone.cpp (Listing Three)--are typical of code generated by Microsoft C's AppWizard. An SDI (Single Document Interface) document-view-based structure is selected during code generation. Phoneview.cpp contains most of the audio handling and user-interface management logic. Talksock.cpp contains the Winsock and protocol-handling components. IGP uses the medium memory model.

The library file GSM.LIB is a 16-bit version of Degener and Bormann's GSM compression module and is available electronically; see "Availability," page 3. The original source-code distribution is included as GSMSRC.ZIP. SOCKLIB.LIB consolidates Mark Clouden's WSNETWRK.LIB and the low-level CSocketOwner and CSockListenServer implementations; these are also available electronically. To run IGP, you will need the included WSNETWRK.DLL and a WINSOCK.LIB and WINSOCK.DLL from your Winsock vendor to link the IGP source code.

Building Future Global Phones

As a technology prototype, IGP shows what is possible through a first-cut functional implementation. There is obviously room for improvement.

First, the user interface could use a face-lift. Management of callee information, prerecorded messages functions, call taping, call screening, and the like could all be readily implemented. Error handling could be made more robust (the current code merely pops up message boxes).

At the engine level, it is possible to implement full packet-based parallel record-compress-transmit with the current architecture. However, this will require extensive testing with boundary and timing conditions. Out-of-band transmission is also necessary to indicate the length of voice segment to the receiver. Once the parallel functions are in place, new application areas--such as voice routers or soft-phone switchboards--will open up.

From the server perspective, parallel receive-decompress-play is possible. However, actual tests over slow links show that synchronization becomes a major issue here. It is difficult to avoid unnatural breaks between received chunks. This undesirable effect offsets any perceived performance benefits.

Simultaneous multiclient handling can be added using the available software-based Wave-Mixer module from Microsoft or third parties. Full-duplex operation (recording and playback at the same time) is not yet possible because of current Windows driver limitations.

Easily adaptable system-level support for audio compression is on the horizon. Looking further ahead, standardization of the still-video-capture API should allow transfer of still, JPEG-compressed frames at the beginning and/or end of audio-segment transmissions. Still, it is important that you not sacrifice the built-in UNIX compatibility. The existing protocol should be adaptable to handle still-frame video in a backward-compatible way.

With the arrival of 32-bit multitasking, multithreaded Windows and the much-needed programmer-oriented enhancements to Winsock beyond the 1.1 specs, most of the complexities encountered in the design of IGP may disappear. Until then, IGP provides a workable two-way voice solution over the Internet for Microsoft Windows.

The WSNETWRK Library

Mark Clouden

Mark is a systems programmer and independent software developer. For more information or to purchase a copy of WSNETWRK, contact Mark at 2632 South Miller Drive, #104, Lakewood, CO 80227.

WSNETWRK is a Microsoft Windows DLL that encapsulates and expands upon the functionality of the Windows Sockets (Winsock) API. I designed the library while working on the development of several nonblocking, Winsock-based applications. Tired of redundant coding involving the handling of network events, I created WSNETWRK as a common base of code that could be maintained independently of the caller. After nailing down the initial functionality, I decided to expand the library. Eventually, what started out as a small collection of standard routines for my own personal use became a full-blown SDK.

WSNETWRK is comprised of a core module, providing base-level socket support, and additional APIs that use the nuclear module to provide Internet standard-protocol support. The current base library contains over 55 routines, including standard operations such as send, receive, connect, and accept. Also included are more high-level functions, such as receives with timeouts, user timer services, socket memory management, out-of-band data handling, and connects incorporating host-name look-up.

The WSNETWRK SDK supports ICMP echo, ECHO, and Finger. FTP client and server support is expected to be available by the time this article goes to print. Also under development for near-term completion are SMTP, POP3, and NNTP. In the long run, all protocols defined as "standard" for the Internet will be included.

A brief discussion of nonblocking sockets and the FD_WRITE event is a good way to show how WSNETWRK works. In order to make true use of a nonblocking socket using Winsock calls, the programmer must change his or her view of how an event drives the application. If a send() is initiated and cannot be completed in a single call, the application must be prepared to request and then process the FD_WRITE event. To process this event, the caller must remember how much of the buffer has been transmitted so far, and then continue the send with another call. The caller must also remember how many bytes were to be sent in order to know when the operation is complete. In most of the applications I have seen, these difficult steps are avoided, and instead the send is completed in a single, tight loop, thereby negating most of the benefits of a nonblocking socket.

Using WSNETWRK to send a buffer requires only a single call to WSSend() (or WSSendTo() for unconnected sockets). WSSend() will return immediately, even though the data has yet to be sent. When the buffer has been completely transmitted (or the send failed with an error), then the application will receive a WSSENDCOMPLETE notification message. To accommodate long operations, WSNETWRK will periodically send a WSSENDUPDATE event to the caller, detailing the number of bytes transmitted at that point. This allows the caller to implement a progress meter or some other user-interface item. An operation in progress may be canceled at any time using the WSCancelIO() function.

WSNETWRK makes use of Winsock's requirement that all network events be sent to the window associated with a socket. WSNETWRK internally creates and manages this window, using it as a method of obtaining the network events. Events received are then processed according to the current state of the socket. As WSNETWRK operations complete, the caller is notified through the WSNETWRK mechanism of a callback function or callback window.

By using the socket's window this way, WSNETWRK can also track when its parent (the calling application) is closed. This allows WSNETWRK to do some garbage collection, destroying sockets and other resources owned by the now-closed application.

Instead of requiring that the caller use the Winsock WSAStartup() and WSACleanup() functions, WSNETWRK instead tracks how each calling task uses it. As a task opens and closes sockets, or makes database requests, WSNETWRK will remember which resources are allocated so that they may be released if the caller exits without closing them itself.

WSNETWRK can be used instead of or in conjunction with raw Winsock function calls. Access to a Winsock development kit is not required. The product saves significant coding time and energy and places no artificial limitations on programs. WSNETWRK is presently implemented as a C-based DLL. Shortly, all modules will be available as VBX interfaces, as well as a C++ class library.

Figure 1 Typical voice/talk application. Figure 2 IGP two-way voice-transmission time delays. Figure 3 Wave-format PCM coding.

Figure 4: Logical threads of operation.

Client Thread
  Become Initiator
  Digitize Voice and send voice

Server Thread
  Become Receiver
  Receive voice and play voice

Listener Thread
  Listen on well-known port for connection
  Spin off Server Thread upon connection

Figure 5 Hierarchy for C++ Winsock client/server classes.

Example 1: (a) Pseudocode for the IGP client; (b) pseudocode for the IGP server.

(a)
while (not end conversation)
     grab a small buffer from built-in dsp
     compress the small buffer
     connect to server tcp socket
     establish protocol parameters
     send the small buffer
     disconnect
end while

(b)
while true
     listen for connection
     if connection then
          establish protocol parameters
          receive a small buffer
          decompress the small buffer
          play the result on built-in dsp
     endif
end while

Table 1: Windows low-level audio routines.

waveInOpen
waveInClose
waveInAddBuffer
waveInPrepareHeader
waveInUnprepareHeader
waveInStart
waveInStop
waveInReset
waveOutOpen
waveOutClose
waveOutPrepareHeader
waveOutUnprepareHeader
waveOutWrite
waveOutReset

Table 2: Protocol states for IGP initiator and receiver.

    Receiver             Initiator

    send ack             open link
    get type of data     wait ack
    send ack             send type of data
    get length of data   wait ack
    send ack             send length of data
    get data itself      wait ack
    send ack             send data itself
    terminate            wait ack
                         terminate

Listing One


//////////////////////////////////////////////////////////////////////////////
// Internet Global Phone Project
// mainfrm.cpp : implementation of the CMainFrame class
//
// Very little modification to the AppWizard generated MFC skeleton.
//////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1993-1994  microWonders Inc.  All rights reserved.
//                                                                         
// AN OPEN INVITION TO BUILD UPON AND CONTRIBUTE TO THE PUBLIC TECHNOLOGY POOL:
// You are encouraged to redistribute, and build upon the technologies 
// presented in this source module and the accompanying article in 
// Dr. Dobb's Journal provided all the conditions listed in the MUSTREAD.TXT 
// file, included with this distribution, are met.
//////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "mphone.h"

#include "mainfrm.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CMainFrame

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    //{{AFX_MSG_MAP(CMainFrame)
             // NOTE - the ClassWizard will add and remove mapping macros here.
             //    DO NOT EDIT what you see in these blocks of generated code !
    ON_WM_CREATE()
    //}}AFX_MSG_MAP
    // Global help commands
    ON_COMMAND(ID_HELP_INDEX, CFrameWnd::OnHelpIndex)
    ON_COMMAND(ID_HELP_USING, CFrameWnd::OnHelpUsing)
    ON_COMMAND(ID_HELP, CFrameWnd::OnHelp)
    ON_COMMAND(ID_CONTEXT_HELP, CFrameWnd::OnContextHelp)
    ON_COMMAND(ID_DEFAULT_HELP, CFrameWnd::OnHelpIndex)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// arrays of IDs used to initialize control bars

// toolbar buttons - IDs are command buttons
static UINT BASED_CODE buttons[] =
{
    // same order as in the bitmap 'toolbar.bmp'
//  ID_FILE_NEW,
    ID_FILE_OPEN,
//  ID_FILE_SAVE,
//      ID_SEPARATOR,
//  ID_EDIT_CUT,
//  ID_EDIT_COPY,
//  ID_EDIT_PASTE,
//      ID_SEPARATOR,
//  ID_FILE_PRINT,
//  ID_APP_ABOUT,
//  ID_CONTEXT_HELP, 
        ID_SEPARATOR,
    ID_PHONE_RECORD,
    ID_PHONE_SEND
};

static UINT BASED_CODE indicators[] =
{
    ID_SEPARATOR,           // status line indicator
    ID_INDICATOR_CAPS,
    ID_INDICATOR_NUM,
    ID_INDICATOR_SCRL,
};

/////////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction

CMainFrame::CMainFrame()
{
    // TODO: add member initialization code here
}

CMainFrame::~CMainFrame()
{
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
        return -1;

    if (!m_wndToolBar.Create(this) ||
        !m_wndToolBar.LoadBitmap(IDR_MAINFRAME) ||
        !m_wndToolBar.SetButtons(buttons,
          sizeof(buttons)/sizeof(UINT)))
    {
        TRACE("Failed to create toolbar\n");
        return -1;      // fail to create
    }

    if (!m_wndStatusBar.Create(this) ||
        !m_wndStatusBar.SetIndicators(indicators,
          sizeof(indicators)/sizeof(UINT)))
    {
        TRACE("Failed to create status bar\n");
        return -1;      // fail to create
    }

    return 0;
}

/////////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics

#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
    CFrameWnd::AssertValid();
}

void CMainFrame::Dump(CDumpContext& dc) const
{
    CFrameWnd::Dump(dc);
}

#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CMainFrame message handlers



Listing Two


// phonedoc.cpp : implementation of the CPhoneDoc class
//
// no change from MFC generated skeleton
//
#include "stdafx.h"
#include "mphone.h"

#include "phonedoc.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CPhoneDoc

IMPLEMENT_DYNCREATE(CPhoneDoc, CDocument)

BEGIN_MESSAGE_MAP(CPhoneDoc, CDocument)
    //{{AFX_MSG_MAP(CPhoneDoc)
             // NOTE - the ClassWizard will add and remove mapping macros here.
             //    DO NOT EDIT what you see in these blocks of generated code!
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

BEGIN_DISPATCH_MAP(CPhoneDoc, CDocument)
    //{{AFX_DISPATCH_MAP(CPhoneDoc)
             // NOTE - the ClassWizard will add and remove mapping macros here.
             //     DO NOT EDIT what you see in these blocks of generated code!
    //}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPhoneDoc construction/destruction
CPhoneDoc::CPhoneDoc()
{
}
CPhoneDoc::~CPhoneDoc()
{
}
BOOL CPhoneDoc::OnNewDocument()
{
    if (!CDocument::OnNewDocument())
        return FALSE;

    // TODO: add reinitialization code here
    // (SDI documents will reuse this document)

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CPhoneDoc serialization

void CPhoneDoc::Serialize(CArchive& ar)
{
    if (ar.IsStoring())
    {
        // TODO: add storing code here
    }
    else
    {
        // TODO: add loading code here
    }
}

/////////////////////////////////////////////////////////////////////////////
// CPhoneDoc diagnostics

#ifdef _DEBUG
void CPhoneDoc::AssertValid() const
{
    CDocument::AssertValid();
}

void CPhoneDoc::Dump(CDumpContext& dc) const
{
    CDocument::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CPhoneDoc commands
//////////////////////////////////////////////////////////////////////////////
// Internet Global Phone Project
// mphone.cpp : implementation file for the IGP application CWinApp class
//
// Very little modification to the AppWizard generated MFC skeleton. Note only
// the mapping of the CWinApp Idle loop to the CPhoneView Idle loop to handle
// the background audio compression/de-compression threads.
//////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1993-1994  microWonders Inc.  All rights reserved.
//                                                                         
// AN OPEN INVITION TO BUILD UPON AND CONTRIBUTE TO THE PUBLIC TECHNOLOGY POOL:
// You are encouraged to redistribute, and build upon the technologies 
// presented in this source module and the accompanying article in 
// Dr. Dobb's Journal provided all the conditions listed in the MUSTREAD.TXT 
// file, included with this distribution, are met.
//////////////////////////////////////////////////////////////////////////////


#include "stdafx.h"  
#include "mmsystem.h"
#include "mphone.h"

#include "mainfrm.h"
#include "phonedoc.h"
#include "wsmin.h"
#include "socket.h" 
#include "talksock.h"
#include "phonevw.h"


#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CPhoneApp

BEGIN_MESSAGE_MAP(CPhoneApp, CWinApp)
    //{{AFX_MSG_MAP(CPhoneApp)
    ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
             // NOTE - the ClassWizard will add and remove mapping macros here.
             //    DO NOT EDIT what you see in these blocks of generated code!
    //}}AFX_MSG_MAP
    // Standard file based document commands
    ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
    ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPhoneApp construction

CPhoneApp::CPhoneApp()
{
    // TODO: add construction code here,
    // Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CPhoneApp object

CPhoneApp NEAR theApp;

/////////////////////////////////////////////////////////////////////////////
// CPhoneApp initialization

BOOL CPhoneApp::InitInstance()
{

    // Standard initialization
    // If you are not using these features and wish to reduce the size
    //  of your final executable, you should remove from the following
    //  the specific initialization routines you do not need.

    SetDialogBkColor();        // Set dialog background color to gray
    LoadStdProfileSettings();  // Load standard INI file options 
                                   //                     (including MRU)

    // Register the application's document templates.  Document templates
    //  serve as the connection between documents, frame windows and views.

    CSingleDocTemplate* pDocTemplate;
    pDocTemplate = new CSingleDocTemplate(
        IDR_MAINFRAME,
        RUNTIME_CLASS(CPhoneDoc),
        RUNTIME_CLASS(CMainFrame),     // main SDI frame window
        RUNTIME_CLASS(CPhoneView));
    AddDocTemplate(pDocTemplate);

    // create a new (empty) document
    OnFileNew();


    if (m_lpCmdLine[0] != '\0')
    {
        // TODO: add command line processing here
    }


    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// Idle Processing for Compression and Decompression

BOOL CPhoneApp::OnIdle(LONG lCount)
{
    BOOL bMore = CWinApp::OnIdle(lCount);
    // do compression or decompression every moment we have  
    if (m_OnlyView)
    {
     bMore = ((CPhoneView *)(m_OnlyView))->DoIdleProcessing();   // neat!
    }
    return bMore;
     // return TRUE as long as there is any more idle tasks

}



/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
    CAboutDlg();

// Dialog Data
    //{{AFX_DATA(CAboutDlg)
    enum { IDD = IDD_ABOUTBOX };
    //}}AFX_DATA

// Implementation
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    //{{AFX_MSG(CAboutDlg)
        // No message handlers
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
    //{{AFX_DATA_INIT(CAboutDlg)
    //}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CAboutDlg)
    //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
    //{{AFX_MSG_MAP(CAboutDlg)
        // No message handlers
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

// App command to run the dialog
void CPhoneApp::OnAppAbout()
{
    CAboutDlg aboutDlg;
    aboutDlg.DoModal();
}

/////////////////////////////////////////////////////////////////////////////
// CPhoneApp commands


Copyright © 1994, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.