                   Speak Freely Development Log

              by Brian C. Wiles -- brian@brisoft.com
        Originally by John Walker  --  http://www.fourmilab.ch/
                                        
23 August 1995

Initial announcement of Speak Freely Release 5.0.

24 August 1995

Peter Claus Gutman (pgut01@cs.auckland.az.nz), developer of a very
nice encryption library, wrote to suggest his library might prove
useful.  In the source code for the library, I found a clever 80x86
assembly-language implementation of IDEA, made freely available by its
author, who is identified in the source code only as "Bryan".  Whoever
you are, Bryan, great piece of work!  If you, or somebody who knows who
you are, happens to read this, let me know so I can give complete
attribution.
  
I integrated the assembly language loop into IDEA\IDEA.C, modifying it
slightly to work with Microsoft Visual C's inline assembler, so you
don't need a separate assembler to take advantage of the optimised
code.  Whether the assembler or original C code is used depends upon
whether USE_ASM is defined, so you can use the original loop for
reference or if, for example, your compiler doesn't support inline
assembly code or is incompatible with the way Microsoft do it.

Enabling the assembly language code increased the speed of IDEA encryption
and decryption on my 486/50 machine from 152,000 bytes per second to 242,000
bytes per second--well worth the trouble of integrating the code.
                                
30 August 1995

Completed a massive revision to avoid all packet fragmentation
and thus work with WINSOCK drivers such as Trumpet WINSOCK.  The
changes were so great and ubiquitous there's no point in trying
to describe them.  In debugging the changes, one of the mysteries
that has been dogging me was finally solved--the random hangs,
loss of synchronism, failure to release resources, etc. etc. etc.
were the result of Windows discarding messages to the main window
as a result of overflows of the default 8 message queue.  Speak Freely
juggles a lot of balls in the air at once, and it's very easy to
hit this limit.  At initialisation time, we now try to expand the
queue to its maximum size of 120 messages or whatever lower maximum
the system we're running on supports.

Added the "Extended Status" (Propeller Head) dialogue.

Made compression modes global rather than per-connection.  This
means compression only has to be done once, which speeds up
party line transmissions.  The change is necessary in any case
so that packet size can be optimised.

1 September 1995

Update release 5.1.

8 September 1995

CreateSocket() in UTILITY.C contained a "defensive bind()" to
address zero as a work-around for some defective WINSOCK implementations.
Unfortunately, this work-around causes other TCP/IP stacks, including
that built into Windows NT to fail.  I made the nugatory bind conditional
on a new Options/Workarounds/Always Bind Socket menu item which is, of
course, saved in the .INI file.

Update release 5.1a.

9 September 1995

Discovered that the reason the socket write was failing on Windows NT
and Windows 95 is that Microsoft's built-in WINSOCK, entirely
incompatible with Unix and every other WINSOCK I have encountered,
refuses sendto() once a datagram socket has been connect()ed.  The
sole function of connect() on a datagram socket is to specify a
default address so subsequent writes can be done with send() (or,
on Unix, write()), and there is no prohibition of overriding this
default address with a subsequent sendto().  The WINSOCK specification
nowhere mentions such a restriction as a Windows-specific change.  I
modified the socket write code in CONNECT.C and the loop-back socket
write in FRAME.C to first try sendto().  If it fails, send() is then
tried and if that works all subsequent socket writes for the rest of
the session are done using send().  This code has been verified to work
on both Windows NT and Windows 95 (first customer shipment edition).
Special thanks to John Deters (jad@DHDSC.MN.ORG) who both identified the
source of this problem on Windows NT and tested innumerable versions
slowly converging toward the actual fix.

Added an item to the Propeller Head dialogue to indicate whether sendto()
or send() is being used to write to outbound sockets; it's "Sending with"
in the "Network" box.

Tested with the WINSOCK implementation included with Sun PC-NFS 5.1.  Works
fine.

Update release 5.1b.

10 September 1995

After last week's experience I decided to indulge in some preemptive
workarounds for crummy network and sound card drivers which fail in
obvious ways which haven't bitten me yet.  I expanded the Options/Workarounds
menu to include:

    Audio
        Assume Half-duplex
            Assumes the sound card is half-duplex without requiring it
            to fail an output open while input is open.  Accommodates
            cards which are actually half-duplex but don't indicate this
            by failing a simultaneous input and output open.  Also handles
            cards which crash the system or application when you try to
            open them in full-duplex mode.
            
        Assume 11025 Samples/sec
            Assumes the card is capable only of 11025 samples per second
            mode, not our preferred 8000 samples per second.  Permits
            correct operation on cards which don't fail when opened with
            a sample rate of 8000 samples per second but which can't
            actually run at that rate.
        
    Network
        Always Bind Socket
            As before; bind outbound sockets, even though there's no need
            to do so.
            
        Never Connect Outbound Socket
            Don't connect() the output sockets.  This implies we'll always
            use sendto() to write to those sockets.  Clears "Use send(), Not
            sendto()" mode if set.
            
        Use send(), Not sendto()
            Always use send() to write to outbound sockets; don't wait for
            a sendto() to fail first.  Accommodates drivers where a sendto()
            on a connected socket crashes the application or system.  Clears
            "Never Connect Outbound Socket" mode if set.
            
All the workaround modes are saved in the SPEAKFRE.INI file and apply to
subsequent executions.  The menu items are disabled when a connection is
active. 

As suggested by John Deters (jad@DHDSC.MN.ORG), I added the ability to
automatically open an iconised version of Speak Freely whenever a new
inbound connection is established.  This lets you see the site that's
just started talking to you.  Since some people might find such an
unsolicited pop-up irritating, this only happens if you check the new
Options menu item "Look Who's Talking".

Tested under Windows 95 final build.  Works fine when using the standard
built-in WINSOCK, but doesn't resolve host names when Sun PC-NFS is
overloaded on top of Windows networking.  This appears to be a general
problem of this configuration; other programs fail on gethostbyname()
in precisely the same way.  When you configure Windows 95, be sure to
install the TCP/IP driver; if you don't you'll get nowhere fast.

12 September 1995
       
Sending a stereo .WAV file in ADPCM compression mode crashed the Unix
speaker program.  The code in READWAVE.C which calculates the number of
bytes of .WAV file needed to fill a packet was incorrectly assuming the
nBlockAlign field was the size of an individual sample, not the frame
of samples for all channels.  Fixed.

Closing a connection while a .WAV file was being sent orphaned the MMIO
handle used to read the file.  Fixed in CONNECT.C.

13 September 1995

Added the ability to drop saved connection (.SFX) files in the MDI frame
window and thereby open (or activate, if already open) connections to
the hosts given in the files.  You can drop multiple connection files in
a multiple selection and each will be opened.

CONNECT.C had its own implementation of DragAcceptFiles() which directly
twiddled WS_EX_ACCEPTFILES.  It doesn't any more.

If a connection file is named on the command line when the program is
launched, it is opened once the application is initialised.  This
permits making an association between the .SFX extension and Speak
Freely in the File Manager and launching the program for a given
connection by double clicking the connection file.  You can specify
multiple connection files on the command line, space separated.  This
allows making a program item icon which opens a collection of
connections, a handy thing to put in your StartUp folder.  (Suggested
by John Gilmore (gnu@toad.com)).

John also pointed out that the program wasn't usable without a mouse
since the left mouse button was the only way to push to talk.  I added
logic in CONNECT.C that permits the space bar to be used to toggle
push to talk, just as in the Unix mike program.  You can cycle between
open connections with Ctrl+Tab and use the space bar to select any set
to which you wish to transmit.

Mouseless users who push to talk with the space bar don't have the
benefit of the cursor change to indicate which connections are
transmitting.  I added a "Transmitting" status indicator in the
connection window which appears whenever live audio is being sent to
the window.

If you make a .WAV sound file with the (nonstandard) sampling rate of
8000 samples per second, it is now played correctly by READWAVE.C, not
forced to the closest standard sampling rate of 11025 samples per
second.  Conversion of stereo .WAV files into mono is still performed
for 8000 sample per second files.  If the user has the ability to make
8000 sample/sec .WAVs, this reduces file size, improves sound quality,
and eliminates CPU overhead when sending such files.  .AU files remain
the fastest, since they're already mu-law encoded.

14 September 1995

Update release 5.1c.

20 September 1995

Began work on answering machine.  Defined structure for data in file,
added a new ANSWER.C module with a function to save a sound buffer
in an answer file in that format.

25 September 1995

Modified the new connection dialogue handler to allow numeric IP
addresses which can't be resolved into host names.  If the host
name lookup fails, the dotted IP number from inet_ntoa is used as
the host name.

Good ole' Trumpet Winsock returns an error status if gethostname()
is called with a buffer too small to hold the entire name, as opposed
to truncating it as Unix does.  I changed the two calls in CONNECT.C
to get the host name in a temporary buffer, then copy as much as will
fit into the sendinghost field of the sound buffer.

Added the ability to set the multicast scope with a new item in the
Options/Connection dialogue.  This item is enabled only if the IP
address is a valid multicast group number. 

Bad ole' Windows 95 WINSOCK returns a WSAEFAULT error if you pass a
single byte argument for the IP_MULTICAST_TTL setsockopt() call.  This
is incompatible with all Unix documentation I have seen.  Trumpet
works correctly with the single byte argument, and accepts the 2
byte short required by Windows 95.  Given the likelihood there's some
other WINSOCK that requires a one byte argument, in goes another
Options/Workaround/Network item: "Multicast TTL Argument Is char" which
does it the Unix way, not as required by Windows 95.

26 September 1995

Added a new Connection/Multicast Groups... dialogue which allows
adding and dropping membership in multicast groups.  Groups can be
specified by DNS-resolvable name or by IP address.  A check box
controls multicast loop-back of locally sent packets to groups
in which this host has added membership.  The loopback box is disabled
on systems (such as Windows 95) which do not implement the IP_MULTICAST_LOOP
setsockopt() option.

1 October 1995

Discovered the multicast tear-down code in the WM_DESTROY message handler
of FRAME.C wasn't testing for a NULL multiName[], resulting in bad
GlobalFree() calls when we failed to initialise a multicast port.  Fixed.

FRAME.C wasn't killing the main timeout timer at WM_DESTROY.  Fixed.

If the attempt to drop a multicast membership at WM_DESTROY time failed,
a message box was displayed as a child window of the one frame being
destroyed.  This is apparently (yet another of the billions and
billgatesillions) undocumented no-no--in any case, if you do it, you
get an "err USER: Attempt to activate destroyed window" at the
time the WM_DESTROY returns.  I changed the parent of the message box
in this case to be NULL and it seems to be happy now.  (In FRAME.C).

Finished implementation of the answering machine, ANSWER.C.  I'll probably
be back before long to make it more message-oriented (select message from
a list box of sites and times, individually delete messages, etc.) but at
least it now has basic functionality.

2 October 1995

Added keyboard accelerator (CTRL+T) for answering machine, and a new
connection menu item that lets you toggle whether incoming messages are
recorded without having to pop up the answering machine dialogue.  Fixed
a bug in which checking or unchecking the record incoming messages box
in the answering machine dialogue didn't take effect until you closed
the dialogue; now it takes effect immediately.

Added code to overwrite the 16 byte session key exchanged via PGP before
closing the file on disc.  Unfortunately, since we can't transmit and
receive the with a pipe, as we do on Unix, there's still a window while
PGP is running during which the session key is visible, but at least this
keeps it from lying around in unallocated disc space for an indeterminate
time.

If no answering machine message file was configured, the answering machine
dialogue in ANSWER.C called scanMessageFile anyway.  Unfortunately, that
routine didn't test for answerFile being NULL and proceeded to stomp all
over memory.  Fixed in ANSWER.C scanMessageFile().

Moved all translatable strings and formats from the .C modules into the
string table of the resource file, using the rstring(), rfilter() functions
and the Format() macro as intermediaries.  Strings that aren't to be translated,
such as fopen() mode strings, formats that contain only a field editing
code, etc. continue to appear as strings in the source code.  Banishing these
strings to the resource file reclaimed almost 4K of data space, enough to give
us some breathing room should it prove necessary to introduce another static
full-size sound buffer for some reason.

3 October 1995

The enabling and disabling of buttons in the answering machine was befuddling
Windows' dialogue box keyboard accelerator logic.  I added code at the end of
a message replay to restore the input focus to the button last pressed or
its logical successor if that button has become disabled as a result of the
message we just completed.

Keyboard accelerators in the answering machine were less than optimally
chosen due to renaming of buttons during its development.  I rationalised
them so the most commonly used buttons have the most obvious keyboard
shortcuts.

Pressing the Close button in the answering machine gave a debug kernel
"err: window destroyed in window callback".  Why, I know not.  It uses
the standard code for modeless dialogues right out of Petzold, which
identical code works perfectly in the propeller-head modeless dialogue.
Changing the DestroyWindow() to a PostMessage of WM_CLOSE to ourself
made the message go away.  I changed the propeller-head dialogue in
DIALOGS.C to use the same logic.

Several modal dialogues needlessly included the system menu in their title
bar.  Eliminated.  (The modal dialogues such as the answering machine and
propeller-head continue to display the system menu.)

Installed help buttons in all the dialogues, linked to the topic
in the help file which describes the dialogue.

Moved the names of our help file and the base Windows help file into
the resource string table.

The Create Key menu item was missing the "..." that indicates it pops
up a dialogue.  Fixed.

The About dialogue was also missing the "...".  Fixed.

I removed the "How to use help" menu item, which has fallen out
of fashion.

Changed "Help/Search..." to "Help/Search for Help on..." as used in current
Microsoft applications.

4 October 1995

Completed moving all section and item titles for the main .INI file and
saved connection files to the string table in the resource file.  Whether
these should be translated isn't clear: a normal user won't ever examine
these files and translating renders them incompatible between different
language editions.  But the saving in data segment size by elminiating
duplication of the section titles alone justifies the work.

Added two new string constants kS0[1] = "0" and kS1 = "1" to FRAME.C and
changed all references to the explicit constants in profile file I/O to
use them.  This eliminates redundant string constants in the data space.

Found a few string constants I'd missed somehow in READWAVE.C.  Banished.

Fixed the answering machine to update the host name when
a definitive name (one not displayed in parentheses) is seen, replacing
any previously displayed name.

Added help butttons to all the file open dialogues, linked to the
appropriate topics in the help file.

Added a pleasant default ring file.  I haven't found a suitable (well-recorded
and public domain) telephone bell, so I decided to pioneer non-irritating
notification of an incoming call with this wind chime derived sound.  The
original appeared on the CD-ROM (N 5) accompanying "News Windows" N 26
(octobre 1995) as the file WINDBELL.WAV.  I used Silicon Graphics' soundfiler
to convert this from an 11025 kHz PCM stereo file to an 8 kHz monaural
.AU file for optimal transmission.

Substantial data space was being wasted by repeated constant references to the
profile (.INI) file name.  I moved this string to the string table in the
resource file and changed the code that loads and saves the global configuration
to load it once into a string on the stack and reference that temporary copy
in all the [Read|Write]PrivateProfile... calls that follow.
 
Added a new MAKEBIN.BAT file in the home directory which builds the binary
release archive.  Now that the release includes more than the .EXE and .HLP
file, something more archival than my fallible memory is needed to make sure

5 October 1995

Remade all screen shots for help file, the addition of the help buttons
required updating all the dialogue bitmaps.

Added logic to the invocations of PGP in FRAME.C and DIALOGS.C to
first try to use the SFPGP.PIF file from the Speak Freely release
directory (obtained with GetModuleFileName) and then, if that fails,
fall back to call on PGP counting on path search to find it.  Going
through the PIF allows the user to override the default modes for
a WinExec call to a DOS program such as PGP, in particular, to run it
in a window, which is much less disruptive of the user's equanimity
than blasting out to a DOS prompt.

6 October 1995

Feature release 5.3.

30 October 1995

All of the Look Who's Listening functionality is working, at least
if you don't push it into reentrancy into Winsock by trying one of
the LWL dialogues while sending or receiving sound.  I'll have to go
back and review the appropriate locks to keep from befuddling Winsock
with actual multitasking.

7 November 1995

Added support for RTP-compatible LPC compression (the Xerox PARC
algorithm developed by Ron Frederick).  This algorithm does a *lot*
of floating point computation (forget it if you don't have a math
coprocessor), and it sometimes mangles sound, especially if you
drive the audio input into clipping or have a high-pitched voice.
But when it works, it achieves better than 12 to 1 compression,
and allows running over 9600 baud lines.  The LPC code is in a new
"lpc" subdirectory.

13 November 1995

Added a first cut "broadcast" facility to permit transmission of
material to multiple hosts (over a suitably fast, probably local
network) without the need to install multicast.  The facility is
relatively crude but should be adequate for uses applications
such as broadcasting meetings across a local network.

The site performing the broadcast simply checks Connection/Broadcast.
Any audio which arrives while Broadcast is checked is sent to every
connected host.  All input events are ignored in connection
windows while a broadcast is in progress, and remotely initiated
connections will not time out during a broadcast.  A user can
subscribe to a broadcast from a given host by initiating a
connection to it and sending a short burst of sound (a second's
worth, say).  This opens a connection on the broadcasting host
to which the broadcast will be sent.  A remote host can unsubscribe
from the broadcast by sending a similar short burst of sound any
time after 10 seconds into the broadcast; the site's connection
on the broadcasting host will be closed 10 seconds later.  The 10
second delay is to prevent toggling of the broadcast state due to
multiple packets being received from the remote site.  Whilst
broadcasting, the application title indicates "- Broadcasting" and
the cursor is always the ear when over a connection window.  When
broadcasting is toggled off, all connection windows are marked
as not being transmitted to and remotely-opened connections resume
the timeout process.

Using short bursts of sound to subscribe and unsubscribe is ugly but
it gets the job done.  Once we have a proper RTP packet exchange
delimiting the connection, it can be replaced.

14 November 1995

If somebody is already blasting sound at us when Speak Freely
is launched, it got all befuddled due to packets arriving
before initialisation was fully complete.  I changed the
WM_CREATE logic in FRAME.C to not enable input on the socket
until initialisation is entirely done.

16 November 1995

Feature release 5.5.

22 November 1995

People seem to get floating divide by zero errors if they try to
use LPC compression on Windows 95.  I added a call to _control87()
in the initialisation code to disable all floating point error
interrupts.  This should allow the LPC code to just bumble along
with infinities and NANs like it does on Unix, which doesn't
seem to do any harm (my suspicion is that this happens only
when the LPC code is fed dead silence).  This will have no effect
anywhere else, since floating point is used only in the LPC code.

I made GSM compression the default out of the box.  I'm deeply weary
of explaining how to enable GSM compression to hundreds of people a
day who can't be bothered to read the help file.

The Phonebook/Search host didn't default to lwl.fourmilab.ch unless
you'd previously made a directory listing.  Fixed.

The Phonebook/Search box wasn't quite wide enough to hold the longest
line of a typical server message and didn't have a horizontal scroll
bar.  This caused perplexed people to send hundreds of E-mails when
they couldn't access the truncated URL the LWL server published.  I made
the box a few characters wider and enable horizontal scrolling.

There was also some ragged logic in the default server for publishing
directory entries.  Fixed to correctly default to lwl.fourmilab.ch.

Update release 5.5a.

28 November 1995

Integrated server-side support for the "show your face" feature.  The
new file FACE.C contains a dialogue that allows the user to designate
a 256 colour .BMP file (the format is verified) as his or her face
image and a function invoked from FRAME.C that delivers blocks of
the image as requested by a remote host.  Processing of face image requests
occurs before audio output is acquired (but after creating a connection),
so half-duplex systems can still transfer face images while sending
audio.

30 November 1995

Integrated client-side handler for "show your face".  Face data
packets are assembled in FACE.C into an in-memory bitmap in the
connection structure.  If a complete bitmap is available, the
WM_PAINT handler in CONNECT.C for the connection window displays
the bitmap instead of the usual status information.  When a bitmap
is displayed in the connection window, transmit state is indicated
by preceding the host name with a small ASCII-art arrow.

After adding hundreds of lines of bullshit Windows code trying to
swap the palette intelligently when two face images are simultaneously
displayed on a colour-mapped display, I decided to exercise that
time-proven prerogative of the Windows developer and just give up.
The vast majority of users won't connect to more than one person
at a time.  I fixed it (with even more bullshit code) so that the
active window is always shown with the correct palette and inactive
windows with the default palette.  If you this this is easy to fix,
baby, just go and try it before you write me some smart-ass
E-mail.  Hint: everything you read in the Windows API documentation
about palettes is a lie or worse when you start to talk about MDI
child windows.  There's a sample application on the Developer CD
which claims to do this, but a glance at it leads me to estimate at
least a week to integrate and test all the crap they went through trying
to do what, on any vaguely competently designed windowing system, should
be essentially transparent to the application.  Users of high-colour
and true-colour display boards will be blithely unaware of any
problem with multiple simultaneous face images.

1 December 1995

Mycal (mycal@monitor.net) reported that messages using simple (2X)
compression were played back at twice normal speed by the answering
machine.  Fixed.

Feature release 5.6.

20 December 1995

Added logic in CONNECT.C to set outputSocketBusy if the send() or
sendto() returns less than the number of bytes we attempted to write.
The WSAEWOULDBLOCK error status is treated as a truncated buffer and
sets outputSocketBusy.  All of this is disabled if the new workaround
"Disable Output Overflow Recovery" is set just, as always, in case.

When outputSocketBusy is set, we're guaranteed by the Winsock spec
that we'll receive a the FD_WRITE notification we requested in the
call on WSAAsyncSelect for the socket.  Right.  So in the timer, should
we discover the socket has become unblocked for output and the fink
didn't tell us, clear outputSocketBusy so things don't hang up.

To avoid output overruns, I changed the logic that responds to face
image requests to ignore requests received while audio output is
active.  This should keep face data from pushing an 14.4 modem connection
that is barely keeping up with GSM over the edge.  The transmission
of the face will be resumed by the timeout on the receiving end
when the audio transmission is done with no harm done.  I also tweaked
the timeouts so they're less likely to collide with one another.

23 December 1995   

Face bitmap exchange seems to hold the potential for bad medicine when
stirred in the pot with multicasting.  When sending to a multicast
port, CONNECT.C now never offers a face to the subscribers, and face
requests received from multicast ports (shouldn't happen, but who
knows: it's Windows!) are ignored and the face retrieval status set to
Abandoned.

Strengthened the .BMP file format verification in FACE.C.  We now verify
that the bitmap has one plane and <= 256 colours, as must be the case
for any compliant bitmap.

Added more stringent verification of the format of received bitmaps in
FACE.C before passing them on to CONNECT.C to display.  This gives us some
protection against rogues who let bogus bitmaps through, and weird
errors in transmission of the the bitmap that corrupt it.

31 December 1996

Added an explicit +armor=off to all invocations of PGP to guarantee the
session key is encrypted in binary mode even if the user has modified
the PGP configuration file to make ASCII armour the default. 

16 January 1996

Integrated the VOX, Break-In, VOX GSM compression, and anti-hangup code
from Dave Hawkes (daveh@cadlink.co.uk).  In the process of testing the
integrated version, I made the following (perhaps temporary) changes:

  *  For some reason, the fix that places the read-only data in ULAW.C
     in the code segment causes Speak Freely to crash with a GPF on the
     first reference to the tables in CONNECT.C, but only when I compile
     in DEBUG mode.  I also needed to include the definition of the new
     tag CONST_DATA to be able to recompile the ADPCM and LPC libraries,
     which contain references to the ULAW.C tables.  As a stopgap, to get
     things running, I changed the definition of CONST_DATA back to
     FAR.  I'll look at this in more detail when I get a chance and see
     if I can't get that data back in the code segment.
     
  *  The anti-hangup code which calls DefaultMessageLoop() every time a
     network packet or wave audio input buffer arrives causes unacceptable
     break-ups of sound on my 486/50; apparently PeekMessage takes too
     long even in the normal case.  I modified MessageLoop() and
     DefaultMessageLoop() in NETFONE.C to save the time (GetTickCount())
     of each pass through the message loop and only run the PeekMessage
     loop if 10 milliseconds or more (constrained, of course, by the
     fundamental resolution of the timer) have elapsed.  I'm hoping this
     will run the risk of the delay only when an actual message loop
     backlog occurs, which runs the graver risk of a lock-up.  I've
     verified that the PeekMessage loop only rarely runs on my
     machine (usually when I block the window by moving it or pulling
     down menus), but since I still cannot reproduce the actual lock-up,
     even when I run my machine at 25 MHz, I can't verify the other side
     of the equation: whether the lock up is still avoided.
     
  *  I changed the "VOX" menu item to "Voice Activation" to avoid jargon
     which might befuddle the radio-nave.
     
  *  Changed the title of the "VOX" monitor dialogue to "VOX Monitor".
     The dialogue isn't wide enough to avoid the "VOX" abbreviation,
     but adding "Monitor" makes it look a little less stark.  It might
     make sense to go to a horizontal meter to justify more room for a
     longer title.

Some people would like to be able to launch Speak Freely from another
application, pointing it a given host with various preset options on
the connection.  Writing an .SFX file naming the host and specifying the
options, then invoking SPEAKFRE.EXE with the .SFX file on the command line
permits this, but the .SFX file had to specify both the host name and
IP address, forcing the calling application to look up the host name.
I modified the newConnection() code in FRAME.C to automatically look up
the IP address if an .SFX file specifies only a host name.

A typo in the Connection/Save code could have led to nugatory void entries
in the .SFX file.  Fixed.

17 January 1996

Added a visual indication when VOX is squelching transmission.  The
ear cursor now changes to an ear with a big X through it (that's
the best I can think of right now, but it's better than no indication
at all).  This makes it a lot easier to evaluate the effect of
VOX speed, especially if you don't have a local machine to run
tests.

Figured what was wrong with the ULAW.C data in the code segment trick.
Apparently CONNECT.C and the libraries didn't get recompiled after the
definition was changed to CONST_DATA, and continued to reference the
Ulaw tables as FAR (as they must).  I changed ULAW.C to explicitly
place the tables in the code segment, but continue to reference them
with a FAR declaration in ULAW.H.

19 January 1996

As suggested by Enoch Wexler (wexler@datasrv.co.il), I added the ability
to send and receive Show Your Face images in GIF as well as BMP format.
GIFs offer substantial compression compared to even the compressed variants
of BMP, which reduces the time it takes to transfer a face image and the
likelihood of disrupting audio transmission in the process.  Received GIF
files are converted in-memory to BMP format by the new module GIFTOBMP
which is based on the NETPBM utility GIFTOPNM.  GIFTOPNM is copyright
1990, 1991, 1993, by David Koblas (koblas@netcom.com), who notes:
  Permission to use, copy, modify, and distribute this software
  and its documentation for any purpose and without fee is hereby
  granted, provided that the above copyright notice appear in all
  copies and that both that copyright notice and this permission
  notice appear in supporting documentation.  This software is
  provided "as is" without express or implied warranty.
  
GIF file decompression requires substantial storage for the LZW
decompression buffers and colour map tables.  I modified the decompression
code to move all large buffers to a dynamically allocated global storage
block to avoid overlow of DGROUP and/or the inclusion of static storage
which would block execution of multiple instances.
  
The face image selection dialogue in FACE.C was modified to allow selection
of GIF images as well as BMP files.

Added a new VOX menu item which calls a new function in VOX.C,
vox_reset_parameters() to restore all the VOX level adjustment parameters
to their original defaults.  I also added a Reset button to the VOX Monitor
dialogue which does the same thing.  In the process, I rearranged the contents
of the Monitor dialogue to create enough room to spell out its title.

Added a new workaround that totally disables the DefaultMessageLoop
lockup-prevention mechanism.  While I think my 10 millisecond trigger
based on GetTickCount() should be enough, this provides an escape hatch
in case it isn't.

23 January 1996

Based on reports that receiving a Ring message can screw up the
sound card (for example, muting the microphone), I demoted the
previous default call on waveOutSetVolume() in SPEAKER.C which
attempts to set maximum output volume when a ring is received
to a Workaround which is off by default, "Set Maximum Volume on
Ring".  Only in the world of Windows would you suppose that
something as innocent as a volume control would conceal sharp
edges and booby traps awaiting the unsuspecting developer.

Based on input from Dave Hawkes, I revised the DefaultMessageLoop code
once again.  This time it keeps track of the last time the program
potentially yielded control to another application (by doing a
PeekMessage with the PM_NOYIELD and PM_NOREMOVE flags in the main
message loop in NETFONE.C, which seems to return fast enough to
avoid pauses, and saving the GetTickCount() value if there is no
message in the queue).  Then, if DefaultMessageLoop() discovers
350 milliseconds or more have elapsed since the last yield, flushes
the message queue using PeekMessage(), which will allow other
applications to gain control of the CPU.

Dave also pointed out that my pointy-headed code that changes the
cursor when VOX muting occurs changed the cursor even if it was
outside the window.  Fixed.

25 January 1996 

The workaround that disables the DefaultMessageLoop() insurance
did not actually turn off all traces of the code--the PeekMessage
in the main message loop in NETFONE.C still remained.  Since I'm
sure this will cause unspeakable horrors when it triggers some
booby trap Billy-boy has hidden in one of his existing products
or is in store for us in the future, I made sure it's
disabled when DefaultMessageLoop() is turned off.

6 February 1996

Made the inclusion of encryption conditional on the tag CRYPTO being
defined in NETFONE.H.  If CRYPTO is not defined, the version number
in the About dialogue will have a suffix of " (no crypto)" and the
IDEA patent notice will be replaced by an explanation of where to
obtain a version including full encryption.  The "Encryption" box in
the Options/Connection dialogue will contain a more detailed explanation
of the no-crypto edition.  The Options/Create Key menu item is disabled
in no-crypto builds.  What's the rationale for this?  Simple: a number
of CD-ROM publishers and sound card manufacturers are interested in
distributing Speak Freely.  But since many will be shipping from
the U.S. and other countries which attempt to restrict the export
of "munitions" like Speak Freely they're afraid, and rightly so, that
putting Speak Freely in the box might result in an all-expenses paid
extended vacation at Club Fed.  The non-crypto version allows them to
include Speak Freely without such worries.  Once a user has installed
Speak Freely and is ready to start using encryption, they can simply
follow the instructions in the dialogue boxes (and, soon, the help file)
and download a full-encryption version from a site in a country which
does not restrict cryptographic software.  The non-crypto version can also
be posted on bulletin board and commercial online services without risking
government-initiated unpleasantness.

Note that undefining CRYPTO does not just block access to encryption
and decryption; it totally removes the code from the program--the
encryption libraries are never referenced and therefore not included
by the linker in the executable.  Thus there is no risk of a non-crypto
build being deemed a munition.  The only "cryppish" code that remains is
MD5, and it is widely used (for example, in the export edition of
Netscape) in non-encryption roles.  In non-crypto Speak Freely, the
RTP SSRC, timestamp, and packet sequence numbers are generated directly
with MD5 rather than the somewhat more random idearand() used in
crypto builds.  Since we're not going to encrypt the RTP packets anyway,
this doesn't compromise anything.

As the first step in integrating the RTP support code from the Unix
version, replaced RTPACKET.C with the fully-functional one and verified
the new rtcp_make_sdes() and rtcp_make_bye() didn't break LWL support.

Here's where we stand at the end of first day of the campaign to integrate
RTP and VAT support into Speak Freely for Windows.  The two procotol
translation modules, RTPACKET.C and VATPKT.C and all their support files have
been included in the program and fixed to compile without errors or warnings.
As noted above, the new RTPACKET.C continues to generate valid packets for
the LWL server.  Sending both VAT and RTP protocol works for all compression
modes, testing with VAT on another machine.  VAT correctly recognises the
VAT ID and RTCP SDES message we send on the control channel.

7 February 1996

Trying to integrate the LIBDES encryption package need for VAT and RTP
encryption blew the data segment, so it's time to run another sweep for
excess baryon particles.  Using the same CONSTANT_DATA trick as in ULAW.H,
I moved the large constant tables in LIBDES\FCRYPT.C and DES\DES.C into
the code segment.  Result: still over the brink.

The biggest memory hog, LPC\LPC.C, was unfortunately not so easily fixed,
since its four large floating point analysis vectors are read/write and
cannot be hidden in the code segment.  I integrated a modified version of
the LPC.C from NeVoT, in which the state of the decoder is in a dynamically
allocated buffer.  I obtain this buffer with GlobalAlloc, getting it out
of the static data segment.  This of course required FARs all over the
place in LPC\LPC.C, but it did the trick.  This will also allow, if we decide
it's worth doing, maintaining a separate LPC state for each inbound connection.

It was intensely irritating to have to constantly answer E-mail from people
who tried to build Speak Freely from source code but whose Winsocks not
only didn't support multicast, their WINSOCK.H didn't even include the
definitions for multicast.  I fixed FRAME.C, DIALOGS.C, and NETFONE.H to,
if IP_MAX_MEMBERSHIPS isn't defined, silently delete multicast from the
build.  If the user's Winsock doesn't define the variables we need to
generate the code, there's no way he's going to be able to use the feature
anyway.  The Unix version uses the same trick to adapt to pre-multicast
sockets implementations.

Swept through the program and added the fProtocol flag to all places
Speak Freely protocol packets are generated.  This flag helps receivers
distinguish Speak Freely packets from VAT and RTP messages.

Went a long way toward implementing DES encryption of outbound RTP and VAT
packets.  It pretty much works--I'll make the final round of tests when DES
works in both directions and I can verify correct operation in both directions.

8 February 1996

I modified code in CONNECT.C and FRAME.C to zero the SDES resend timer
when the transmit protocol or encryption key is changed.  This causes an
immediate resend of the SDES/VAT ID in the new mode, which will help the
receiver to "sync up" with the change.

I discovered that the clever way I integrated VAT and RTP encryption into
sendpkt() in CONNECT.C had completely screwed up encryption for Speak
Freely protocol.  In the process of fixing this, I cleaned up some of
the rather tangled logic in that function.

Added code to the Options/Connection dialogue to disable the fields and
captions for IDEA, PGP, and Key file compression if the protocol is
RTP or VAT.  These protocols currently specify only DES as a standard
mode.

Set the "talk spurt" flag for the first packet of a sound file.

Added VAT packet translation to sound file output in CONNECT.C.

Under certain circumstances, sending a sound file to a connection after
sending a ring would just re-send the ring.  Fixed in FRAME.C.

Disabled direct modem connections.  This feature, which fell into the
trapdoor called Windows serial port (8250) support, compounded the incoming
E-mail pain due to idiots confusing direct dial-up modem connections with
SLIP/PPP Internet access.  Besides, since Serial I/O is near the top of
the Redmond Kiddies' "API of the Year" list, the time it takes to debug
it on all the ratty drivers out there exceeds the product life cycle.
Users who wish to use Speak Freely as a phone scrambler on direct calls should
establish a peer-to-peer TCP/IP connection and use Speak Freely in network
mode.  Since it will probably take Billy's bozos 20 or 30 years to debug
Windows to Windows TCP/IP links, the fact that they'll blame their screwups
on other vulnerable applications as well as Speak Freely will
deflect a significant percentage of flames, albeit not to the flamers
responsible for the mess in the first place.

9 February 1996

Voice activation didn't work with RTP and VAT protocols because
load_vox_type_params() in VOX.C didn't know about the new packet sizes
used by those protocols.  Now it does.  Independently, LPC and VOX
don't seem to be getting along very well together, regardless of
protocol.  I'll have to look into this later on.

Coming to terms with the fact that I'll be chasing "bugs" in this
program as long as there are Kode Kiddies in Redmond, I integrated
the hex dump module from the Unix version.  It lets me dump packets
on the debug stream to see where Winsock wants to go today.

Transmission of non-encrypted VAT and RTP packets now seems to be
working.  That's not to say that DES encryption doesn't work, just
that I haven't tested it yet.  The initial tests of bouncing VAT
messages off the echo server failed due to byte order dependencies
in VATPKT.C.  These are now fixed; the changes must now be integrated
into the Unix stream to handle little-endian boxes.

10 February 1996

Added code to the WM_DESTROY handler in CONNECT.C to transmit an RTP
or VAT BYE message to indicate the user has closed the connection.  In
the process, I added a new sendSessionControl() function which is used
by both this logic and the periodic RTCP/VAT ID transmission code in the
timer.

Discovered that the sockets were getting closed in WM_CLOSE rather than
WM_DESTROY, which kept the BYE transmission from working.  I moved the
socket close to after the BYE is sent in WM_CLOSE.

11 February 1996

Well, I think I finally found out why weird things occasionally
happened when you quit Speak Freely with one or more connection
windows open.  This is really getting too depressing to document, but
here we go.  Windows, with its unerring instinct for doing things in
the most idiotic way possible, sends a WM_DESTROY to the application's
outer window procedure, and then *later* sends WM_DESTROY to each of
the child windows.  Suppose one or more of those child windows need to
do something--send a BYE message, for example--using one of the
resources that get freed by the application's outermost WM_DESTROY?
Blooie.  But don't think you can get away with just sending WM_DESTROY
to each of the child windows: nopey, nopey, no.  If you try that you
fall into the toilet because calling WM_DESTROY doesn't actually make
them go away, and as a result they get destroyed twice and all kinds
of other horrors ensue.  So, once again we are forced into subterfuge
by the quintessential inelegance of Windows.  We dig out of this
particular hole by sending a custom WM_CLEAN_UP_YOUR_ACT message to
the child window to tell it we're terminating.  The child will then do
its regular WM_DESTROY cleanup, including releasing the client data
pointer and zeroing the window word to it.  When the actual WM_DESTROY
arrives, it will discover the client data pointer is zero and avoid
executing the cleanup twice.

12 February 1996

Integrated the new LWL\LWL.C library from the Unix version, which allows
a separate decoder state for each receiver and contains numerous fixes for
subtle coder gotchas such as dividing by zero if total silence is received.
This, of course, ran squarely into one of the innumerable floating point
code generation bugs in Visual C++ 1.5 (they call it "Visual" because only
if you're seeing things would you confuse it for a production compiler for
floating-point intensive code).  After a fine afternoon of trying various
compiler options and workarounds, I found a combination of restructuring
of the loop which caused the "Stack overfl" (a very Redmond kind of error
message, don't you think?) and optimisation options which got around the
error.  Until the next "improvement" of the compiler, I'm sure.

Found another pair of missing htons() in the LPC packet handlers in VATPKT.C
and RTPACKET.C, when stuffing the decoded length into the first two bytes
of the sound buffer.

Memo to file.  Windows' real-time response is so pitiful that machines
which are perfectly adequate to run Speak Freely protocol in all compression
and encryption modes (a 486/50, for example) can't cope with the smaller
packet sizes used by RTP and VAT, particularly on reception.  If you want to
talk to somebody who can only send RTP or VAT, you'd better make sure you
have enough Intel inside to cope with Billy Boy's idea of process switch
latency.

13 February 1996

Integrated a fix to rtpout() in RTPACKET.C from the Unix version.  The
packet length for outbound RTP ADPCM packets was 2 bytes short, which
caused "gravelly" speech and horrible ticking when encryption was
enabled.

It's possible for the predicted value in the ADPCM coder (ADPCM/ADPCM_U.C)
to exceed the range of a signed 16 bit linear sample.  Clamping code
limits the range when this happens, but needed to declare the unclamped
sample as a long rather than int to work on a 16 bit architecture.
Fixed.

After many, many hours of painful, unremunerated toil I
finally figured out what was causing the Debug kernel
warnings and fatal errors due to bad pointers at the
time we call WSACleanup at application termination time.
The essential clue was that it only happens if one or more
connection windows have been created by the receipt of a
packet from a remote site not already connected.  If all
connections were created locally it never happened.  And, of
course, this only happened under the Winsock supplied with
Sun PC-NFS 5.1--the problem never occurred under any
circumstances on other Winsocks.

I'm sure by now you will be shocked and stunned to learn that Sun
NFS 5.1 doesn't correctly implement the WSAAsyncGetHostByAddr
function.  Oh, you can make the call, and you even get back a
valid host name.  But doing so plants a time bomb which will kill
you (at least under the debug kernel) much, much later when you
call WSACleanup() right before exiting the program.  At that
time, depending on where the random pointer inside their
so-called WSHELPER points, you get either two invalid global
pointer errors or a fatal error due to an object usage count
underflow in the (bogus) global block.  If the
waNetSynchronousGetHostnameAction is set, we eschew the
asynchronous request and make the user wait for a blocking
gethostbyaddr() which has the merit, at least, of not blowing us
away at program termination time.

waNetSynchronousGetHostnameAction is set, in turn, based on the
workaround waNetSynchronousGetHostname, which can take on the
values 0, 1, and 2.  If 2, the default, asynchronous host name
retrieval is disabled if the Winsock identifies itself in the
szDescription field of the WSAData structure returned by
WSAStartup() as "Sun Select PC-NFS Windows Sockets
Implementation".  This automatic selection can be overridden by
the user explicitly checking or unchecking the Options /
Workarounds / Network / Get &Host Name Synchronously menu item.
Thereafter, the user's selection will be used regardless of the
identity the Winsock reports.

In the process of adding profile variable support for the above workaround,
I observed the number of workarounds was about to exceed the rstring()
cache in the name of the workarounds section remained.  Since, unlike the
developers of Microsoft tools, I do not feed off human suffering and take
joy in setting booby traps, I modified all the profile read and write
code to copy the section name to a stack string variable rather than rely
on the pointer within rstring()'s retrieval area to remain valid while all
variables in the section are accessed.

14 February 1996

Integrated the new RTPACKET.C, RTPACKET.H, and DESKEY.C from the Unix
version.  These include the facilities we'll need for parsing SDES
packets, recognising BYEs, and creating RTP keys from key strings compliant
with RFC 1890.

Adjusted packet sizes returned by inputSampleCount() (FRAME.C)
for VAT to the maximum permitted within both the experience base
of VAT and the 512 byte guaranteed MTU of Winsock.

Integrated a fix from the Unix version to guarantee (in our context) that
pad bytes added to VAT and RTP packets are zeroed.

Modified makeVATid() in VATPKT.C to, as VAT does, prefer the user's
full name to the E-mail address if both are available.

Added recognition of RTP and VAT SDES/ID packets in FRAME.C.  The title
of the connection window will now show the user's name, if supplied
by the sender.  The changeAudioState() function in CONNECT.C also uses
the user name, if available, in preference to the host name when it updates
the window title to indicate transmit state.

RTP and VAT BYE/DONE packets now cause the receive protocol to be reset to
PROTOCOL_UNKNOWN.  This expedites recognition of a new protocol if the
sender switches on the fly.  Changed in FRAME.C.

Integrated generation of RFC 1890 RTP key and separate old-protocol
VAT key.  I've still to integrate automatic protocol and key sensing.

Added code to encrypt outbound RTP and VAT packets with the appropriate
key.  The inbound side remains to be done.

Nailed another encryption packet size rounding error in CONNECT.C, this
time affecting ADPCM encoded outbound packets in VAT protocol.

15 February 1995

I remembered that there was one more place the PC-NFS 5.1
WSAAsyncGetHostByAddr() bug could stab us in the back--in the case
where the user enters a numeric IP address in the Connection/New
dialogue.  I added a gethostbyaddr() alternative to this call if
waNetSynchronousGetHostnameAction is set.

Finished integrating automatic protocol sensing and key selection
for encrypted inbound RTP and VAT packets.  The logic was a little
less tangled than in the Unix version since we process control and
data packets in different callback functions.

Did a non-CRYPTO build to make sure all the RTP and VAT changes
didn't break something or suck in verboten bits.  Sure enough, all
of DESKEY.C needed #ifdef CRYPTO, as well as the encryption code
in CONNECT.C's sendSessionControl().  Fixed.

Implemented the guts of the local loopback facility--it works, but
tuning and a nice user interface remain to be done.  Why local
loopback?  So users can debug their audio hardware problems before
venturing onto the net, which will be one of the steps in the "beginner's
guide to Speak Freely" I'll get around to writing one of these days.
Eventually there will be a Help menu item which creates a local echo
connection, but for the moment you activate such a connection by
making a new connection to "localhost" (or, if your Winsock doesn't know
localhost from Casper the Friendly Ghost, 127.0.0.1).  Any packets
you send are saved in memory until the end of your transmission and
then returned, after a short delay, as if echoed by a remote site.
This is, then, an echo server that doesn't use the network.  Not only
does it let users experiment with audio hardware locally, it allows
isolating network-induced problems from those which inhere in the
CPU or audio hardware.

16 February 1996

Modified changeAudioState to invalidate the connection window without
erasing the background.  This makes it quicker to repaint the "Transmitting"
or blank status when the audio state changes.

Added support for Speak Freely SDES messages on the control port.  When
a Speak Freely connection is open, RTCP SDES messages with a protocol ID of
1 (the old RTP, used by no application I know of) are sent on the control
port.  These messages allow unambiguous recognition of Speak Freely protocol,
transfer of user information, and disconnect notification.

The connection window paint code in CONNECT.C now displays the current
sending protocol, user name, and E-mail address of the connected user.  The
connection window is resized depending on the number of lines currently
displayed.

17 February 1996

Added the ability to specify a port number for a connection.  This required
changes all over the place:

        *       A port number can now be entered in the Connection/New dialogue
                after the host name or IP number, delimited by a slash.
        *       The port number (even if standard) is saved in an .SFX file by
                Connection/Save / Save As.
        *       A port number, if specified, is restored when a connection file is
                loaded.  If no port number appears in the file, the default of
                2074 is used.
        *       The "Connect" button in the Look Who's Listening dialogue now
                passes both the IP address and port number, separated by a
                slash, as the known host argment to newConnection in FRAME.C,
                which was modified to recognise that syntax.
        *       FRAME.C now maintains a list of auxiliary receive sockets,
                asList.  When input arrives, a new function, findPort() searches
                the list to identify, from the socket number, which port the input
                arrived from.  This is used, if we're creating a new connection based
                on the input, to set the port to which we'll respond.
        *       CONNECT.C now uses the "port" field in the connection structure as
                the port to which messages are sent rather than the canned value of
                2074.
        *       findClientByHost() in FRAME.C now considers two connections identical
                only if both the IP address and port numbers are identical.
        *       When creating a new connection with a nonstandard port number, CONNECT.C
                calls the new function monitorPort() which creates an auxiliary socket
                pair for that port.  If the port is already monitored, the reference
                count on the auxiliary socket is simply incremented.
        *       When destroying a connection to a nonstandard port, CONNECT.C decrements
                the reference count on the auxiliary socket and if it's zero closes
                the socket pair.
                
Note that auxiliary sockets are not bound to a specific host; once a
connection is established with a given port, connections from any host
can be remotely initiated on that port.  This means that if you want to
accept connections on a given port as a matter of course, you can do so
simply by opening a dummy connection (to a nonexistent address on your
subnet, for example) with that port.  I'll probably eventually add a
separate dialogue that lets you specify ports to monitor automatically
but for the moment this gets the job done.  Most users will be specifying
ports to connect to remote RTP and VAT conferences anyway, not accepting
calls on nonstandard ports.

Guess what?  Sun PC-NFS 5.1 Winsock will not only blow you away if you
call WSAAsyncGetHostByAddr(), the blocking version, gethostbyaddr() has a
bug in it as well--it forgets to null-terminate the host name in the h_name
field, so if you retrieve a host with a name shorter than the last one
part of the last host name still sticks out.  Working around this by
zeroing the host name after you retrieve it is a blatant violation of
the Winsock spec which states (section 4.2.1) "The application must never
attempt to modify this structure or to free any of its components.".  I
for one, am not going to add my name to the list of millions who ignore
the Winsock spec, so there isn't a damned thing I can do this other than
tell people to get a better Winsock.  Fortunately, it's purely an
ugliness that doesn't do any damage since we're just retrieving the
host name to display in the connection window.

Oops!  In Speak Freely protocol, control channel messages aren't supposed
to be encrypted but they were.  Fixed.

What the world needs now, is lots more workarounds, they're the only thing
that drive the bugs to ground....  So, some more anticipatory retaliation:
the following are available on the new Options/Workarounds/Protocol submenu.
All are, of course, saved in the .INI file and otherwise treated as
respectable citizens.

        No Speak Freely Heartbeat
                Disable the periodic Speak Freely protocol heartbeat on the
                control channel.  This is primarily intended as a last resort
                if the (less than 1%) added bandwidth saturates a close to the
                edge connection, and also in case the control channel packets
                awake something horrid lurking on the next higher channel.
                
        Large RTP Protocol Packets
                Uses Speak Freely's preferred packet sizes for GSM and LPC
                compression rather than those typically sent by RTP programs.
                Most RTP programs were developed on fast workstations with high
                bandwidth network connectivity.  Speak Freely users generally
                have slower machines and network links which benefit from larger
                packets.  Try this if the person you're talking to reports halting
                audio in RTP protocol.

        Disable VAT Protocol Detection
                VAT protocol will never be automatically selected as a result
                of receiving a message on the control channel which resembles a
                VAT control message.  Enable this if you never receive VAT protocol
                messages and are annoyed at how long it takes to identify the
                protocol of encrypted RTP messages.

        Disable RTP Protocol Detection
                RTP protocol will never be automatically selected as a result
                of receiving a message on the control channel which resembles a
                RTP control message.  Enable this if you never receive RTP protocol
                messages and are annoyed at how long it takes to identify the
                protocol of encrypted VAT messages.
                
        No Encryption of RTP Control Packets
                RTP control packets can, according to the standard, be sent either
                encrypted or in the clear.  Most RTP programs I've encountered
                encrypt their control packets, so this is the default Speak Freely
                sends (it accepts both encrypted and clear packets).  If you set
                this workaround, control packets are sent in the clear.

19 February 1996

After further deliberations, I decided not to implement automatic protocol
switching to the protocol received from the active window, although much
of the infrastructure to do so is in place.  The reason is that simply
adding the new dimension of multiple protocols has the potential for
inducing further confusion among users who don't understand the
distinction between the compression mode received and that used in
transmission.  Trying to explain all the possible conditions one could
get into with automatic protocol switching is probably futile.  I may
eventually put in a warning that pops up if the user tries to transmit
to a connection which has sent us packets in a different protocol than
the current transmit protocol.  Protocol mismatch is never a problem
when communicating with other copies of Speak Freely, since it auto-senses
the protocol.  Since initially relatively few users will be talking to
other programs, those cutting-edge users are probably best encouraged
to operate in "manual transmission" mode to avoid confusion.

The gimmick that forces immediate transmission of the identity message
on the control channel (rather than waiting for the next timer interval)
wasn't doing so when the protocol is set to Speak Freely.  Fixed in
FRAME.C.

Drat!  When I added the port number criterion to decide whether a
connection was already open, I forgot to handle local loopback.  Fixed.

Added direct pointers from the Help menu to the FAQ and Mailing List
sections of the Help file, and to create local loopback connection
directly.

Direct access to local loopback required a tweak in CONNECT.C to not
attempt to turn the loopback IP address into a host name.

20 February 1996

sendSessionControl() in CONNECT.C wasn't incrementing packetsSent for
the extended status dialogue.  Fixed.

loop_sendto() in LOOPBACK.C wasn't returning SOCKET_ERROR and setting
the last error code as it should if it can't allocate the loopback
buffer.  Fixed.

To eliminate the choppiness that afflicted local loopback, particularly
with the small packets sent by VAT protocol, I modified loopback replay
to adopt a strategy of keeping the output queue stuffed with packets
up to a limit of 10, and refilling the queue to that length every 10
milliseconds (Hah!!  More like when Windows gets around to us.) rather
than attempting to time each packet to a time resolution Windows
just can't handle.  (If I used the multimedia timer, it probably could,
but that requires interrupt code call-backs into a DLL and imposes
restrictions on what we can do from the call-back that Speak Freely
couldn't live with.)

Naturally, this straightforward approach walked squarely into the
jaws of disaster.  The message loop insurance code was causing the
timer code to be re-entered while it was playing back loopback
packets, setting off a spectacular riot of recursion.  I disabled the
message loop insurance for loopback packet playback.  Since we're
strictly controlling the rate of packet arrival from loopback and
the length of the stream is limited anyway, we don't really need
the message loop insurance, which is intended to keep packets arriving
from the network from hanging us.

21 February 1996

I added some code to the MM_WIM_DATA message handler in FRAME.C to
soften the impact of the anti-lockup code on outbound audio quality.
If a machine is right on the ragged edge of being able to compress in
real time (a 486/50 sending GSM, for example), occasional Windows-induced
delays will trigger the anti-lockup code and cause a sound packet to
be dropped.  I added code that allows recovery from one re-entry to
the message handler by saving the packet and processing it immediately
after the already-underway packet.  Re-entries while an already saved
packet awaits processing continue to discard packets.

The anti-lockup code in MM_WIM_DATA and socketInput did not increment
the appropriate PacketLost counters.  Fixed.

Added an item to the Help menu that points people directly to the echo
server topic in the help file.

Building on Dave Hawkes' insight that the Windows wave audio output pause
and restart could be used to implement a de-jittering replay delay with
no buffering logic within Speak Freely, I implemented a first cut at
de-jittering.  Any input packet from the network which causes us to
acquire audio output is considered the start of a "talk spurt".  (Once
we've transitioned to RTP, we can use the packet header bit for this, but
we have to get there somehow.)  When such a packet is received, if the
jitterBuf has a nonzero replay delay in milliseconds, a timer with that
expiration is launched to trigger the replay and wave audio output is
paused.  Wave audio output is restarted when the timer expires, or if the
number of packets queued for replay exceeeds half the number of messages
in the input queue (detected in SPEAKER.C).  A new Options/Jitter Compensation
menu item allows specifying the initial delay for a talk spurt.  The longer
the delay, the greater the suppression of jitter, but at the cost of a greater
time parallax between the reception of the packet and its being played on the
speaker.

When shutting down audio input, the final partial packet of sound before
the shutdown could be lost.  Fixing this little buglet naturally required
massive changes to how audio input is torn down, since Windows likes
to return packets from the queue in any old order at shutdown time, but
imposes on the application a rigid order in which the API must be
called.  The terminateWaveInput() function in FRAME.C now actually does
no such thing.  In fact, it just resets audio input, causing any partial
packet and the rest of the input queue to be returned to the message loop.
Code for the MM_WIM_DATA message now processes any partial packets,
padding them if necessary to the length prescribed by the protocol (with
the correct pad depending on whether audio is 8 or 16 bits--gosh this is
fun!) and sends any non-zero-length packets.  If termination is underway,
packets are unprepared and released, and an allocated packet counter
decremented.  When that counter goes to zero, wave audio is finally
actually closed.

The above fix of course broke how Options/Break Input manages the transition
between input and output mode for half-duplex audio hardware.  Fixed (I think,
pending reports to the contrary from the field).

22 February 1995

One more tweak to Break Input--if inputPaused is set, the WM_MIM_DATA
handler in FRAME.C now immediately discards any partial packets that
are returned during input termination.  This speeds up the transition
to playing the packets arriving from the socket.

26 February 1996

Port numbers greater than 32767 were not accepted in Connection/New due
to being scanned as a signed short rather than unsigned.  Fixed.

The supposedly private bits used by the answering machine to mark the start
of a transmission conflicted with the fProtocol flag bit, resulting in
each Speak Freely protocol packet being considered the start of a separate
message by the answering machine.  Fixed.

29 February 1996

Dodging another intracardial dagger from our south-of-the-equator purveyors
of what purport to be WINSOCK drivers introduces another layer of unnecessary
and unwarranted complexity.  The WINSOCK spec allows mutant windsuckers to
abort any call that "re-enters" WINSOCK with a WSAEINPROGRESS call.  Fine:
what would you expect from the "vision of the future is a reboot in the face
forever" people?  But could you imagine, even in your wildest fantasy, that
the most innocent of all socket calls, the one which *places* a socket in
non-blocking mode, could *itself* blow off if a so-called blocking call
(and many calls so-deemed have no non-blocking variants) is in progress?
So, I turned the code that sends the heartbeat to the Look Who's Listening
server inside out to cope with this crap, and thereby avoid the dreaded
"Operation already in progress" puke-o-rama which, on some WINSOCKs poisons
all future network accesses.  I am sure this will have ugly consequences on
other buggy platforms which will become apparent in the weeks and months
to come.

Failure to look up the host name corresponding to an IP address after a
connection was made displayed an error dialogue.  Little did I know that
95% of all Windows 95 users do not have a valid domain name server
configured, and each and every one of them E-mail me when this message
appears.  Warning message deleted; don't keep those cards and letters
coming.

1 March 1996

Integrated the changes to VATPKT.C from the Unix version to recognise
IDLIST packets as valid to to provide, in the future, the ability to
include a conference ID in the packets we send.

Integrated the fixes from the Unix version into FRAME.C to recognise
VAT IDLIST (3) packets and correctly parse the user names therein.
This allows us to connect in VAT protocol to CU-SeeMe reflectors.
As part of this change, the user name field (uname) in the connection
structure was made a dynamically allocated buffer to permit long lists
of participants in a conference.

I modified the connection creation logic in controlInput() in FRAME.C
to never create a new VAT protocol connection unless an ID (1) message
is received.  This keeps IDLISTS (3) which rain in from conferences you've
just left from re-opening the conference connection.  Also, it was
inelegant to open a connection based on a VAT BYE from the blue.  A
remote VAT connection will be opened only upon the receipt of an
unencrypted ID packet.  Note that we still have a problem with VAT
conference Lazarus connections which result from VAT packets which
arrive on the data port and are mistaken for pre-6.0 Speak Freely
sound packets--they won't be played, but they'll still open the
connection.  This will go away once we've gotten everybody on 6.0 and
require control port session control unless a special workaround is
set to communicate with older versions.

Added logic in the connection window WM_PAINT handler in CONNECT.C
to distinguish a VAT IDLIST (and one of these days, a multi-party
RTCP SDES) from a simple ID and list the users in the conference one
one per line.

After an afternoon of flailing around that sacrified the requisite
number of neurons, I finally figured out a way to handle a user
quitting the program while audio output is active which does not
lead to sudden death.  This involves the usual flags, mushy timers,
countdowns and activity tests which Windows requires to do even the
simplest things, and is much too depressing to discuss here.  If
you must see it, start at the WM_CLOSE handler in FRAME.C and follow
the trail of slime through the rest of that file.

Echo, voice on demand, and reflector servers all have a tendency to
create "Lazarus connections" which, seconds after you close them,
pop back into existence when a packet comes back from the other end.
To prevent this, I added a special anti-Lazarus mechanism in FRAME.C
and CONNECT.C.  When the user closes a connection window, the
WM_CLEAN_UP_YOUR_ACT message handler in CONNECT.C saves the IP address
of the host in a new global Lazarus and sets the timeout counter
LazarusLong to LazarusLength (15 seconds as presently configured),
which is decremented by the main one-second timer in FRAME.C.  If
a packet arrives from the last connection window to be closed while
LazarusLong has not yet counted down to zero, is it discarded by
code in socketInput and controlInput in FRAME.C before it causes
the connection to be reestablished.  This provides a "decent interval"
to allow postmortem packets arriving from the remote host
to be discarded without re-opening the connection window.

To avoid the ignominy of shipping a release containing OutputDebugString
diagnostic output, I added an updated version of the check for
debug output in a production build that's used in Home Planet.  The
new version provides a primate-readable description of the error that
points to the offending line and doesn't interfere with compilation
of the rest of the file.

2 March 1996

Fixed a place in isHalfDuplex() in FRAME.C where in the case of an error
in the process of determining whether audio is half duplex, the wave
format buffer could be freed twice.

For some reason, trying to compile the program on a Pentium with twice
as much free RAM as the 486, the resource compiler dies in the middle
of windowsx.h with "Out of far heap".  I excluded this file from resource
compiler builds, and the little pointy head now deigns to work.

The VAT IDLIST packet parsing in FRAME.C's controlInput() had an
off-by-one error when the name string length was a multiple of 4 and
the terminating '\0' fell into the next 4 byte segment.  Fixed.

One final gratuitous Gatesian gutshot this fine Saturday night--Virtual
(if you confuse it for a real compiler, you're seeing things) C 1.52c
generates bad code for the VAT IDLIST packet parser in controlInput()
(FRAME.C) when full optimisation is selected.  (I'm sure, gentle reader,
this will surprise you, having come this far with me down the rathole.)
So, I #pragma'ed off all optimisation in that function, wishing there were
were some way I could #fragma the "making it all too much" crowd spewing
their ghastly gigabytes into an industry I was once proud to be a part of.

6.0-Alpha 4 prerelease.

3 March 1996

Integrated lots of fixes all over the place for 32 bit compile problems.
I made these changes in a very conservative manner--what the compiler
sees when compiling the 16 bit version should be identical to the
code before the fixes were installed.

4 March 1996

People were having so much trouble getting the automatic adaptive VOX
to work that I disabled it and replaced it with a manual set-the-level
yourself mechanism.  The VOX Monitor dialogue now allows you move the
red threshold indicator with the scroll bar with complete freedom, while
(if audio input is live) showing the VU meter as before.  Slow, Medium,
and Fast continue to regulate the number of samples of silence which
must be seen before transmission mutes.  There is no need with a manual
VOX adjustment for a Reset facility, so the menu item and button in
the monitor dialogue for that function were removed.  I also disabled
VOX GSM compression mode, since I'm afraid explaining its interaction with
manual VOX would only confuse people.  All the code is still there for
adaptive VOX and VOX GSM compression--if you compile with VOX_GSM defined,
it will all come back.

Just as I suspected, the workaround for Trumpet Winsock's WSAEINPROGRESS
bugs in contacting the Look Who's Listening server walked right into the
jaws of another flaky Winsock--this time the one that comes with Windows
95.  You apparently can't count on it to always notify you when a socket
is closed, even if you've requested such notification via WSAAsyncSelect().
This led to timeout warning messages, attempts to make socket calls on
already-closed sockets, and other horrors.  Throwing up my virtual hands in
disgust, I ripped out all message-based event sequencing of the LWL socket
in FRAME.C and replaced it with timer logic.  This is a crazy way of doing
what should be trivial, but it's the only way to guarantee (to the extent
one can ever use that word in conjunction with something associated with
Windows) we won't be nailed by timing windows, lost notifications, or other
flaky behaviour on the part of Winsock.

5 March 1996

Well, that didn't work on a production build under PC-NFS, because a blocking
connect (to a slow-to-respond LWL server) could interfere with data
transmission.  So (and I have a very, very bad feeling about this), I made
LWL transmission entirely non-blocking.  When we receive the FD_CONNECT
notification, that triggers the send() and sets the timer to close the
socket 8 seconds later (since we don't dare count on linger mode to
work properly).  Wanna bet we need a timer to back up the FD_CONNECT
notification in case Winsock forgets to send us one?  We'll see.

Added some additional paranoia in LWL.C to make sure no traffic is sent
to the LWL server while a non-blocking transmission is in progress.

6.0-Alpha 5 prerelease.

7 March 1996

It's reported that connecting to a VAT conference with 35 people active
causes a "runtime error 202 at 0001:1E".  I don't have the vaguest idea
what is causing this, what the error number means, or even where the error
message is coming from, and I can't reproduce it since I don't have access
to multicast to get on such a conference.  I am not going to let nonsense
like this deny the 6.0 update to tens of thousands of users who will never
go near anything like this.  I just hammered a test in CONNECT.C that limits
the number of participants displayed to 8 and puts an ellipsis at the end
of the list if there are more than that.

Added code to multicastJoin() in FRAME.C to join or drop any auxiliary sockets
to the current multicast list, as well as the default port sockets.

Modified monitorPort in CONNECT.C to call multicastJoin to drop and then
reacquire the multicast memberships whenever a new auxiliary socket is
created.

8 March 1996

Once again we see how the poor design of Windows turns what is conceptually
an easy task into a snowdrift, nay polar caps, of tangled and tricky code
that one is never really sure will work everywhere.  This time it's double
clicking in the connection window to begin continuous transmission (which
a lot more people will be doing now that we have VOX and Break Input).
Remember back on the 21st of February when I redid the logic of how audio
input is switched off so as dodge various bullets in the multimedia
complex?  Well guess what...ever since then double clicking hasn't worked (I
didn't notice this since I generally use the space bar).  Now, the double
click logic in CONNECT.C couldn't have been simpler or more compliant with
the Windows interface guidelines--the second click merely extends the
scope of what the first one does, and does so simply by not switching off
input on the button up event following a double click.  Ahhh, but recall
that you can't just turn audio input on and off like a light switch.  It
*takes a while* for the input buffers to rattle through the message queue
and all the assorted dust to settle.  So when we received the double click and
subsequent button up event, audio input was still in the process of being
terminated (as a result of the first button up event), and that's no time
to go and re-open it.

What to do?  Well, here comes another brutal hack necessitated by irrational
Microsoft design.  When we process an WM_LBUTTONUP in CONNECT.C, we no longer
close audio input to the connection.  Instead, we set a *timer* (does this
sound familiar?) running to expire GetDoubleClickTime() after the button
was released.  If we see a WM_LBUTTONDBLCLK an its subsequent WM_LBUTTONUP
before the timer expires, we revoke the time with KillTimer() and leave
audio input active.  If the timer goes off without our having seen further
mouse action, audio input to the connection is shut off at that time through
the expedient of faking a space bar input to the message loop.  This is
spectacularly ugly but it gets the job done.

12 March 1996

I fired up the Large Software Collider for another day of flailing away
with Visual C++ 4.0 under Windows 95.  I discovered that the last single
remaining piece of non-paranoid code in RTPACKET.C, the part that innocently
stores the 16 bit length into RTCP SDES items, ran afoul of Visual C++'s
talent for invariably making the wrong choice when given the slightest
latitude for doing so.  In this case we had a structure with several
bit fields which added up to 16 bits followed by an unsigned short.  Visual C++
padded the bit fields (which we are not use anyway, since it would store
them in backwards order) to a 32 bit boundary before generating the
short.  I changed the code to use hard-coded byte numbers and casts
everywhere, essentially treating Visual C++ as an assembler, which is
approximately its intellectual level.

Integrated recent fixes from the 16 bit development stream to bring the
source streams into sync.  From now on the 32 bit version on the LSC will be
the primary stream, with the 16 bit version automatically recompiled
from the common source.

In the process of being led down the garden path by a bug in
BoundsChecker "Compile-Time Instrumentation" (GlobalFreePtr() in
windowsx.h in WIN32 generates bogus bad handle messages), I
cleaned up some code belch in FRAME.C by eliminating the
dynamic allocation and free of the PCMWAVEFORMAT structure in
various functions.  The structure is small; there's no reason
not to just put it on the stack and get rid of all the error
tests, frees on various paths, etc. ugly etc.

Simple file I/O is so ubquitous in programs that it was an
irresistable target for the Giant Rat of Redmond.  Now you're
supposed to open files using CreateFile function (elegant choice
of name, don't you think), which takes 6 pages to document
in the API book.  The old offically sanctioned way of dealing
with files, _lopen(), _lread(), etc., which worked perfectly well,
are still there, but they've gratuitously deleted the definition
of READ_WRITE, which was how you were supposed to specify that
mode.  I examined WINDOWS.H for Windows 3.1 and discovered that
OF_READWRITE, the new officially sanctioned name, has the same
numerical value and is defined in 3.1's WINDOWS.H, so I just
changed the references in CONNECT.C and FRAME.C to use that
symbol in order to compile on both 16 and 32 bit without an
ugly #ifdef.  This, and removing an incorrect earlier fix which
called _sopen (yet another entirely incompatible way of doing
I/O) instead of _lopen, got sound files and ring working on
32 bit.

14 March 1996

The Look Who's Listening dialogues weren't working because they
were testing for edit control notifications using hard-coded
Win 3.1 notification codes.  I changed them to use WM_COMMAND_NOTIFY,
which fetches the notification code from the correct place for
both 16 and 32 bit builds.  Fixes were needed in both LWL.C and
DIALOGS.C.

The handling of the WM_MDIACTIVATE message in COMMAND.C had to be
revised due to the "packing" change in its arguments between
Win 16 and 32.  After threading through the lies and disinformation
provided by what purports to be a guide to porting code to 32 bit
Windows (found on the current MSDN CD-ROM), I fixed the code to
work correctly on both 16 and 32 bit.

Found and "fixed" (in other words uglified) two more places in
RTPACKET.C where the straightforward code from the RTP standard
ran afoul of Visual C++ 4.0's eccentric ideas about structure
alignment (which, as far as I know, no other 32 bit compiler
seems to share).

When I disabled modem support, I forgot that CRC.C was used only
by the modem and didn't disable it as well, which wasted a little
data segment space.  Fixed.

To avoid future disasters stemming from unwarranted trust in
Microsoft compilers, I #ifdef-ed out the definitions of the
RTP and RTCP packet structures in RTP.H, leaving them for documentation
only.  Guess what?  Another bunch of field references popped out in
LWL.C and RTPACKET.C.  Most were harmless (at least on VC 4.0), but
the ones that weren't explained why LWL searches weren't working.
So, as far as I can tell, LWL publish and search are now both
working again.

BoundsChecker was nattering about how we terminate the main MDI
frame window at exit time.  I added a handler for WM_NCDESTROY to
zero the hwndMDIClient handle so a (harmless) "bad window handle"
is not passed to DefFrameProc() in FRAME.C.

Who you gonna call?  BoundsChecker!!!  Staked a memory leak in the
processing of VAT IDLIST packets in FRAME.C.  If an IDLIST packet
was identical to the last displayed, it was never released.  Fixed.

Memo to file: BoundsChecker will report several memory leaks of
buffers allocated by malloc() by the various libraries.  These are
nothing to worry about, as they are allocated on the local heap and
do not leave resources in use after Speak Freely exits.  There is no
reason to complicate the termination code purely to appease BoundsChecker.

15 March 1996

When I made the WAVEFORMAT structures automatic rather than dynamically
allocated, I forgot to delete the error message for allocation
failure.  It's gone.

If something blew up in processing at WM_CREATE time, it's possible
the WM_DESTROY handler could try to walk through the MDI windows using
a NULL MDI client window handle.  Fixed.

The WM_CREATE handler, onCreate() in FRAME.C, returned 0 in case
of error, not -1 as specified by the Windows API book.  Heaven
knows what you are really supposed to do since returning 0,
the documented value for success, actually terminates the application.
So, I return 1 just like I used to, which has the merit of working.

Under WIN32, our WinMain() function in NETFONE.C is not informed
if a copy of Speak Freely is already executing.  In order to implement
the trick of double clicking an .SFX file to open a connection in
an existing copy of Speak Freely, I had to add a FindWindow() call
which searches for a preexisting window with our class name to
detect if Speak Freely is already running.  Because socket ports and
audio hardware are non-shared resources, it is not possible to run
more than one copy of Speak Freely at a time.

Connections established on the command line with non-standard ports
were not working because the auxiliary socket must be created after
the hwndMDIFrame window creation is complete.  I moved the command
line processing from the main window onCreate() in FRAME.C back to
INIT.C so it's done after the frame window is created and its
handle therefore available.

Added an indication to the title in the About dialogue as to whether
this is a 16 or 32 bit version.

If something screwed up in the process of creating a connection which
displayed an alert, it was possible for the not-yet-initialised
connection to receive a timer message which caused it to try to send
a heartbeat with the protocol not yet specified, which referenced a
NULL pointer.  Fixed.

16 March 1996

Transmission of face images to remote hosts was broken in 32 bit
because I accidentally used _lseek instead of _llseek in FACE.C to
position the read pointer in the face file.  You can get away with
this on Windows 3.1, but not in 32 bit.  Fixed.

Modified the code that paints connection windows in CONNECT.C and
the MDI child window class definition to adopt the "new look" of
Windows 95 dialogue boxes.  The old connection window looked too
garish alongside "grey flannel" Windows 95.  This only happens for
32 bit builds.

Turned off some no-longer-necessary debug mode code from SPEAKER.C
which could conflict with the display of faces and participants
in a multi-party conference.

No call to desdone() was made at application termination time, which
orphaned several buffers allocated by desinit().  Since these were
local storage allocated by malloc(), no harm was done, but I added a
call to desdone() to get rid of the natters from BoundsChecker.

Discovered that several of the libraries were being explicitly loaded
from their home subdirectories rather than being dynamically selected
based on the configuration.  This explained why DES encryption wasn't
working for Speak Freely protocol--the current library was never
loaded!  I fixed the search paths so that the current configuration
libraries will always be used.

Deleted the "Bounds Checker" configurations which led to gnarly ~n
ZIP names.  If I decide to try Bounds Checker compile-time instrumentation
again (like, when it really works), I'll use a portable directory
name like BOUNDER.

The code which protects against OutputDebugString()s left in
release version ran afoul of WIN32's defining this as a macro
for its own evil purposes.  I added a #undef to get rid of of any
preexisting macro so our version will prevail without warning
messages.

17 March 1996

Can you believe it?  If you make an association between a file type and
your application and double click on a long file name like
"D:\Connections\Jean-Pascal Bauer.sfx" your application gets launched
with the file name on the command line *not quoted*!  Blown away is
twenty years of Unix and MS-DOS convention that command line arguments are
separated by spaces.  Thanks, Billy boy.  Okay, we have to handle this
somehow, so I changed the command line parser in INIT.C and FRAME.C
to, on 32 bit builds only, use a comma as the delimiter between
multiple connection files on the command line.  I am quite certain that
Murkysoft will add quotes around long file names in a future "upgrade",
at which time every application which programmed around the current
idiocy will stop working.  I suppose one could anticipate this dagger
from the future and parse quoted file names in the present, but I am
not going to stoop to speculative defensive programming.

I discovered the reason we were getting (harmless) "Can't open include
file messages" when attempting to re-scan dependencies was the zany
way Build/Settings/Resources parses the list of include directories.
I twiddled the list delimiters until it generated a correct command
line to eliminate the natter.

All the help buttons in the dialogues were broken due to the negative
ID_HELP (used to avoid having a different help ID for every dialogue,
thank you very much resource compiler) being widened to an int without
sign extension in the WM_COMMAND handler.  I added a cast to (short)
to restore proper sign extension in both 16 and 32 bit builds.

Made a new SF32.HPJ file in the help directory that allows recompilation
with the Visual C 4.0 HCW help compiler.

Remade the help file screen shot bitmaps for Windows 95 appearance.
These are kept in a new HELP\BMP32 directory which is searched first
by SF32.HPJ.

18 March 1996

Added a new Help/Performance Benchmark dialogue which displays
performance of the various compression and encryption modes as a
percentage of the real-time sample rate.  The goal is to help people
decide which modes their computer can handle and to evaluate different
optimisations, compiler code generation options, etc.

Integrated the NSA LPC-10 codec into a new LPC-10 subdirectory and
added code to SPEAKER.C to support decompressing packets received
in LPC-10.  Transmission isn't implemented yet.

19 March 1996

Two references to multicastJoin in CONNECT.C weren't disabled when
IP_MAX_MEMBERSHIPS isn't defined, indicating a brain-dead Winsock
that doesn't support multicast.

LPC-10 compression on output is now integrated into CONNECT.C.

Added LPC-10 compression to the performance benchmark.

21 March 1996

Integrated a fix from the Unix version for bad decryption when both
a key file and another encryption mode that requires padding to an
8 byte frame.  Since the padding has been done before the key file
encryption is performed, key file decryption in SPEAKER.C needs to
pad to guarantee the entire frame for subsequent decryption is
properly decoded.

Integrated the new (slightly) improved Simple compression module,
RATE.C, from the Unix version.

25 March 1996

Hitting Esc or clicking the "X" button didn't close the Performance
Benchmark dialogue because there wasn't an IDCANCEL case in the dialogue
procedure.  Fixed.

Added a Help button to the Benchmark dialogue with appropriate link
to the topic in the help file.

6.1-Alpha 1 prerelease.

3 April 1996

The new simple compression code (RATE.C) broke the answering machine's
replay of simple compressed packets.  For various historical reasons,
all poor, simple compression was handled differently than all other
types in SPEAKER.C: simple compressed packets were not expanded in
place as for GSM, LPC, etc., but left compressed when written to the
answering machine and decompressed again when played.  This was fine
prior to RATE.C, since no state information was needed to perform the
decompression, but with the new algorithm the connection data structure
must be available to decompress and it is not, of course, around when
one plays back a message from the answering machine.  I
modified the simple decompression code in SPEAKER.C to work like all
other forms of compression and expand the sound buffer in place, then
removed the hack from ANSWER.C which allowed the fComp2X bit to pass
through to the answering machine file in answerSave().  (Reported by
Marc de Groot, marc@immersive.com).

Deleted some long-obsolete debugging code in SPEAKER.C, already
turned off with #ifdef OBSOLETE.

A race condition existed in ANSWER.C which could lead to a General
Protection Fault from an access through a null pointer if the
answering machine was either popped up or closed while audio was
being received from the network.  There was a possibility that
answerSave() would decide the answering machine dialogue was up
because hDlgAnswer was non-NULL, then try to add the new message
to msgTable before the dialogue procedure got a chance to allocate
the table.  I added a test in answerSave() to only consider the
answering machine dialogue up after it has finished its
WM_INITDIALOG processing and the message table is allocated.
(Reported by Marc de Groot.)

Sending with PGP encryption failed when creating the session
key temporary file due to an incompatible change in the GetTempFileName()
function in Win32.  Now you have to call GetTempPath() to find out
where to stick the temp file.  The obvious upward-compatible way
to handle the 16 bit Windows API call clearly eluded the "Monkey-C,
Monkey-doo" back at the slug ranch.  (Reported by John Deters,
jad@dsddhc.com).

The same temporary directory diddling had to be done in pgpSetSessionKey()
in FRAME.C handle the New Official Temporary Directory Nomenclature Mechanism.

4 April 1996

Okay, I get it now...the reason the MS-DOS window hangs with the
annoying alert box is that PGP was being run directly rather then
through SFPGP.PIF, which is marked "Close on exit".  The PIF was
not being used, in turn, thanks to the fact that under Visual C 4,
the program you're working on is executed from the WinDebug or WinRel
(or whatever) directory, not the project directory where SFPGP.PIF
lives.  This shouldn't be a problem in the production version since
the .EXE and .PIF will be in the same archive.  To facilitate testing
of the development version, I copied SFPGP.PIF into both debug and
release directories.  Automatic PGP encryption and decryption of
session keys now seems to work OK.

After a day chasing the mysterious floating point error on a 486DX-33
when running the LPC decompression phase of the benchmark (reported
by Bill Oatman (BillO@lpa.com), and correlating what was seen in
Speak Freely with the sporadic reports of floating point errors in Home
Planet which appear on 486DX machines as soon as a user installs
Windows 95 (even if Home Planet has been running perfectly on the
same machine for years under Windows 3.x), I am now persuaded there is
something wrong in the management of the coprocessor/FPU control word
related to Windows 95, which perhaps manifests itself only on slower
(and maybe therefore earlier step revision) 486DX machines.  Please
allow me to explain....  According to Mushysoft's documentation for
all C libraries dating back to Visual C 1, when a program receives
control, all floating point exceptions are masked; in other words the
FPU generates a NaN, Infinity, etc. instead of causing an interrupt.
Here's the statement from the documentation for the _control87() function
in the Visual C 4 online help:

        Note The run-time libraries mask all floating-point
                 exceptions by default.

Pretty unambiguous, huh?  But if you believe that, then there is no way
you can get a floating point exception interrupt at all, unless you
use _control87() to deliberately unmask one or more exceptions which,
I can assure you, I'm not doing.  I *did* build a version of Speak Freely
which unmasked exceptions and discovered that the exception which
popped out of LPC.C's uncompressing phase was a completely harmless
denormalised and/or underflow where the default (exception masked)
behaviour of the FPU is precisely what is intended.

But this particular folder of the Hex Files now contains a number of
unambiguous reports from a variety of people, affecting two different
applications, which contradict the expectation of no interrupts.
Naturally, this problem does not manifest itself on any machine I
have tested on, including a 486DX.  A search of Mickeysoft's product
support database for all the obvious keywords, examination of the
known bug list for Visual C++ 4.0, and errata for the 486 on
Intel's Web site came up blank on anything seemingly related to a
problem of this nature.

What to do?  Well, like every other time something completely
impossible happens on a Mangysoft platform, there's little one
can do other than pile on defensive workarounds and add instrumentation
to pin down the bug.  In this case, I added a call on
_control87() in the application initialisation code in WinMain()
(NETFONE.C) which explicitly masks all floating point exceptions,
even though they're supposed to already be masked when we receive
control.  In addition, I added temporary code, executed
when you start the performance benchmark, which verifies that the
FPU is in the correct modes according to the value returned by
_control87(0, 0).  If it isn't, a warning is issued and an attempt
made to reset the modes to all masked.  If this fails to do so, a
second warning is issued.  The output from these debug message
boxes should, on a machine which exhibits the failure, point much
more directly at the source of the problem.

August 10, 1997 -- Brian C. Wiles

  Brian C. Wiles took existing Speak Freely source code, and began his
modifications.

  Added outgoing message to answering machine, a feature
that should have been there a long time ago when the answering machine
was added.

  Eventually, if I can't find source code out there for a 32-bit video
phone, I will be adding that to Speak Freely.  Maybe we should call it
"See Clearly".  I'd love that!  Of course, it's probably already taken.

November 30, 1997 -- Brian C. Wiles

  I added a conference feature which was sorely lacking in John Walker's
version of Speak Freely.  It allows you to talk to multiple people at
the same time without having to use multicasting.

  I took the broadcast idea, and added another mode similar to it.  The
main difference is that it does not close a connection or open a new one
when someone sends it a sound packet like it does for broadcast mode.
This allows people to talk to you while you're sending data out to others
at the same time.

  Here are the differences between broadcast and normal mode as compared
to the new conference mode, this giving a clearer picture of the
shortcomings of each mode and what conference mode offers:

        Differences from normal mode

        *  Sends to multiple clients at the same time.

        Differences from Broadcast mode

        *  Plays incoming sound.
        *  Won't disconnect clients or subscribe new clients.

  I still have to test this with 2 other people, but it works well with
2 echo servers.  In my test conferencing rpcp.mit.edu and
echo.fourmilab.ch, I found GSM compression will work for about 10 seconds
on a 28.8 modem until it overflows the modem, causing a long silence.  LPC
compression works good, except when 2 people are talking at the same time,
it has a lot of pops in the sound playback.  LPC 10 compression works the
best, but has lower quality than GSM compression.

  Next, I want to adapt Speak Freely to blend incoming sounds together, much
like combining wave files, so that the sound plays back like a real
conference call.

April 25, 1998 -- Brian C. Wiles

  Contacted John Walker, and asked for OK to take over Speak Freely
project since it had been 2 years since he last updated it.  He
agreed.

April 26, 1998 -- Brian C. Wiles

  Changed About box to Release 7.0 beta 1.

October 3, 1998 -- Brian C. Wiles

  Added Echo Mode to act like echo server.  Also added defines for
toggling certain features such as broadcasting and the new (barely
tested) conference and echo modes.

October 23, 1998 -- Brian C. Wiles
        *** 7.0 beta 1 ***

  Cleaned up source for beta distribution on web site.  I still need
to add file browse dialog for answering machine outgoing message.

December 4, 1998 -- Brian C. Wiles

  Fixed a bug where if the answering machine's outgoing message
couldn't be opened, multiple errors would pop up at the rate of once
per second without stopping.

  Added a check to make sure the outgoing message was only sent once
per connection.  This also had the added effect of fixing the above
bug.

  Added a browse button and better descriptions for the answering
machine outgoing message.  There is no need to have an outgoing
message, but it will be sent if it exists.

December 5, 1998 -- Brian C. Wiles

  Fixed bug in GIF reading code in DoExtension() that would read
forever instead of properly stopping at the end of a GIF89a extension
block, causing an illegal operation.  This would happen for any GIF
image that contained things like transparancy information or
comments.  Thus, this problem would occur on a lot (if not most) GIF
images used as face files.  I'm guessing that this will fix a big
chunk of the crashes that SF users have reported.

  Added better implementation of the SpeakICQ patch for ICQ to use
Speak Freely.  The new function also checks the current path of the
Speak Freely executable and prompts the user if they want to update
it.  As long as Mirabilis doesn't change the format they store the
registry keys in, this should eliminate the need for any separate
patch or any other user intervention.

December 6, 1998 -- Brian C. Wiles
  *** 7.0 beta 2 ***

  Updated the help file (with Notepad so it wouldn't "corrupt" it) to
change the web site address and fix a couple missing words in the
introduction section.

January 25, 1999 -- Brian C. Wiles
  *** 7.0 beta 3 ***

  Added user's full name to connection profile in case nickname is
blank from ICQ.  This was suggested by Birger Fricke to fix the
"connection profile is invalid" error some ICQ users were
experiencing.

2 March 1999 -- John Walker

Based on sporadic reports that Windows 9x refused to load programs
flagged with a native language other than that of the user's own
locale, I modified all the resources to be flagged "Neutral" so
they should work on any locale whatsoever.  Previously, they were
flagged as "French (Switzerland)", not because I requested such
a constraint, but because always-helpful Monkey C took it upon
itself to declare that anything produced in that locale must be
restricted to use therein.  Now if Microsoft products only worked
within a 100 km radius of Redmond...but I jest...they don't even work
acceptably within their own corporate campus.

Added a new Blowfish subdirectory containing the eponymous
encryption toolkit, based on the SSLeay package.  Blowfish is
now a sub-project of the Speakfre workspace, and is included in
the C and Resource include paths and the link library search
paths.

Implemented Blowfish encryption mode.  A 16 byte key is
generated from the given key string as for IDEA, and the separate
Blowfish key can be saved in the connection file.  As before,
Blowfish can be used in conjunction with any other set of encryption
modes (but not with VAT or RTP protocols, as they do not presently
support that form of encryption).  Due to horrid overlaps in the
Options/Connection dialogue, I'll need to hand-edit the .rc file
to restore a rational tab order to the items in it--you can always
tell a GUI: it's sticky when you touch it, and it makes you regret
ever going near that tar-baby.

6 March 1999 -- John Walker

Added Blowfish to the benchmark dialogue in bench.c.  In the process
I discovered that the encryption benchmarks were inconsistent in how
they accounted for key generation time--some set the key in the inner
loop while other set it only once per run.  This can make a significant
difference for an algorithm like Blowfish where key generation is
deliberately slow and complicated in order to make actual encryption
or decryption faster.  Given that in normal operation Speak Freely
only generates a key for a connection one time, when the key is
specified by the Options/Connection dialogue, I decided to standardise
the encryption benchmarks to only generate one key per run.

Cleaned up some gnarly Win16 _fmemset, etc. stuff in bench.c.

Changed some unnecessarily named items in the benchmark dialogue
definition in the resource file to IDC_STATIC.

Deleted unused symbols in resource includes.

Deleted unused handlers for two obsolete menu items in Frame.c.

7 March 1999  --  John Walker

Finished adding text chat support.  Here's how it works.
Text chat occurs in a new modeless dialogue whose parent is
the main MDI frame window, hwndMDIFrame.  The dialogue
procedure and support code is in the new file Chat.c.
The dialogue contains a multiline read-only text edit box
in which the conversation occurs and a single-line input
box where the user can compose lines to be sent.  When
the user presses Return in this input line or presses
the Send button to its right, a new WM_CHAT_TEXT_SEND
(in the WM_USER range) is sent to the hwndMDIFrame
window (Frame.c), which then iterates over all its child
connection windows and forwards the WM_CHAT_TEXT_SEND
message to them.  The connection windows (Connect.c),
in turn, on receiving the WM_CHAT_TEXT_SEND message,
assemble an RTCP APP message of type RTCP_APP_TEXT_CHAT
containing the text and call sendSessionCtrl() to transmit
it on the control port.

On the receiving end, controlInput() in Frame.c detects
APP messages with a content name of RTCP_APP_TEXT_CHAT,
copies the payload to a newly allocated string buffer, and
chatLog() in Chat.c to display it in the scrolling area.
chatLog() is passed the best available user identity for the
connection from which the chat text arrived, in descending
order: user name, E-mail address, host name, or IP address.
If the text chat dialogue isn't already displayed, chatLog()
launches it, adds the received line to the scrolling area,
and releases the string buffer.

Text chat is supported only when sending in Speak Freely
protocol; a warning is displayed if you try to send chat
text when the protocol is set to RTP or VAT.

Moved strings in the ICQ setup in Init.c which may need to be
localised to the string table in the resource file.  Strings
which do not require localisation (registry keys, etc.) remain
in the source file.

8 March 1999 -- John Walker

The code in Chat.c which copied text chat received from another site into
the transcript area wasn't properly obtaining the length of the
text already there, which could result in text appearing at other
than the end of the transcript.  Fixed.

Commented out the definition of CRYPTO in Netfone.h and added it to
the project-specified includes for Debug and Release builds.  Created
a new "No Crypto Release" configuration which is identical to Release
except it does not define Crypto.  The permits maintaining Spook Freely
in its own build directory without the need to modify the header file
when it needs to be updated.  Naturally, this required an hour of manual
labour creating new configurations for each of the libraries included
in the non-crypto build and entering rational settings for each, since
Monkey C can always be counted on to default any value to something
idiotic.  Intermediate and output files for non-crypto builds will
be found in the "Nocrypto" directory of each source directory.  Note
that none of the subdirectories actually contains code which depends
on the setting of CRYPTO, but I know of now way to re-use the
libraries from a Release build without hammering in explicit path
names.

When I tried a non-crypto build I discovered I'd been a bit too
aggressive in getting rid of "unused" resource symbols--I deleted
the tags used to disable the crypto-related title strings in the
performance benchmark dialogue (Bench.c).  I put the symbols back,
and added Blowfish to the fields disabled for a non-crypto build.

Release 7.0 beta 4.

10 March 1999 -- John Walker

The code in Frame.c's MM_WIM_DATA handler which attempts to
recover from overruns processing wave input data contained two
subtle errors which could be encountered only when queueing
failed and a buffer actually had to be discarded.  The discarded
buffer was not placed back onto the queue for wave input, and
after being discarded the code failed to break from the processing
sequence.  If anybody actually encountered this error, they
probably wouldn't notice because its symptom would have been
identical to that of the situation it's intended to handle--break-ups
in transmitted audio due to a CPU not fast enough for the selected
compression and encryption modes.  Fixed.

19 March 1999 -- John Walker

Modified Idea/Idea.c to define IDEA32 on WIN32 builds, which
got rid of 12 (harmless) compiler warning messages.  In theory
this should speed up IDEA on 32 bit processors, but according
to the benchmark it made little if any difference.

Added prototypes and casts to eliminate all the warning messages
when building the Lpc10 directory.  Almost all of the warnings
were harmless implicit narrowing in assignments, all of which
are now explicitly cast.

Cleaned up harmless compiler warnings in the LPC directory and
removed some Win16 gnarl which can now be dispensed with.

Cleaned up harmless compiler warnings in the GSM directory.
Speak Freely now builds with any warnings on Monkey C 5.0
with Warning Level 3.  Most of the libraries build with no
warnings at level 4, but since Microsoft's own Windows header
files generate hundreds of warnings at this level, it is
impractical to adopt that level for the project as a whole.

Modified InitApplication() in Init.c to use the gimmick whereby
you can specify a background brush colour as a COLOR_xxx + 1
and have RegisterClass create the brush for you.  This gets rid
of a harmless (and incorrect, as a matter of fact) natter from
Bounds Checker.  Besides, it's simpler to do it this way.

Added code to the WM_DESTROY handler in Frame.c to release the
LWL and RTP SDES packets as well as the VAT ID packet.  This isn't
really necessary (and wasn't even on Win16, since the packets in
question were allocated with malloc(), which meant they were
in local memory), but it gets rid of three more natters from
Bounds Checker.

The WM_DESTROY handler in Frame.c wasn't performing a closesocket()
on sCommand and sControl, the sockets it uses to listen for packets
from the network on the data and control ports.  This didn't do any
harm, but it did create a Bounds Checker natter since the first
socket() call allocates a small buffer which isn't released until every
open socket has been closed (the so-called WASCleanup() can't be
bothered to clean up this buffer, evidently).  I added ResetSocket
calls for these two sockets, guaranteeing that they're closed and the
buffer released, solely to get rid of the squawk.

At this point, the only remaining Bounds Checker nit is a buffer
allocated within the C library function tzset() which is never
released by design.  The only way to get rid of this is to replace
all references to the <time.h> functions with their ugly Win32
equivalents.  I'll pass.

When a local loopback connection was closed, loop_flush() in Loopback.c
was called before the control port Bye message was sent, creating an
orphaned buffer (which did no harm, but irritated Bounds Checker).  I
moved the loop_flush() in the WM_CLEAN_UP_YOUR_ACT message handler in
Connect.c so that it has a chance to release the Bye message.

20 March 1999 -- John Walker

Added "Speak Freely: " prefix to the titles of all dialogue boxes
in which it will fit.

Made the Modem Settings dialogue and the two "Modem Rant" dialogues
conditional on "MODEM" being defined.  This will keep them from appearing
as resources in normal builds.  I'll rip them out entirely when I get
around to liquidating the ill-starred MODEM connection code.

The code which edited the length of the output queue in the Extended
Status (Propeller-head) dialogue played a perfectly legal but slightly
underhanded trick of swapping in a format with no edit phrase for
the queue length when the queue was empty, still including the queue
length argument in the wsprintf() call.  Unfortunately, this apparent
discrepancy was caught by Bounds Checker, with the unfortunate consequence
that it popped up an error box every time the Extended Status dialogue
was updated and the output queue was empty.  All these interruptions got
in the way of testing other aspects of the program under Bounds Checker,
so I re-phrased the code in question to appease Bounds Checker.  The same
fix was also required in the MM_WOM_DONE message handler in Frame.c,
which decrements the output queue length and updates the Extended Status
dialogue if it is displayed, and also in Speaker.c which increments the
queue length when adding a sound buffer to it.

21 March 1999 -- John Walker

Added Audio Monitor parameters to the speakfre.ini settings file so they're
remembered from session to session.

Cleaned up some more Win16 legacy gnarl in Frame.c.

22 March 1999 -- John Walker

Ripped out obsolete Win16 MakeProcInstance from Bench.c, Chat.c, Dialogs.c,
Face.c, Frame.c, and Lwl.c.  Changes to references to MakeProcInstance
objects required modifications to Answer.c and Spectral.c as well.

Phonebook/Search in Lwl.c wouldn't submit a blank query when the
Return key was pressed with the query string in focus, but would
if the Search button was pressed.  One may now submit a blank query
either way.

Ripped out VOX_GSM code in Dialogs.c.  We're never going to use it, so
why go on carrying it around?

29 March 1999 -- John Walker

Added backing bitmaps to the spectrum and energy envelope displays
in Spectral.c.  This cleans up the paint code and guarantees a fast,
complete repaint if the window is exposed after having been obscured.

Deleted the now-obsolete NETFONE.DEF file.

Removed obsolete VOX_GSM code from Vox.c, Vox.h, Frame.c, and Speaker.c.

Ripped out some more Win16 gnarl in Ulaw.c and Ulaw.h.

Removed Win16 code from Netfone.c, Netfone.h, Speaker.c, Dialogs.c,
Connect, and Init.c.

Deleted some disabled and long obsolete code in Lwl.c.

31 March 1999 -- John Walker

Made propeller-head average energy and power spectrum scale display in
the Audio Monitor dialogue conditional on _DEBUG being defined.

Release 7.0 beta 5.

2 April 1999 -- John Walker

As reported by Brian C. Wiles, one of the string table messages for
ICQ setup was truncated because it was longer (108 characters) than
the buffer in rstring() in Utility.c to retrieve it.  I increased the
buffer in rstring() to 132 characters.  Also, rstring() incorrectly
tested the result from LoadString as negative when a string is not
found (this should never happen, of course).  I corrected it to
use the proper status of zero for a missing string.

3 April 1999 -- John Walker

When displaying audio received from the network,
playSound() in Speaker.c was passing the data to spectrumUpdate()
at the most convenient point--when all decryption and decompression
is done, at which time the sound buffer is known to contain 8 bit
mu-law samples.  Unfortunately, due to jitter compensation, delays
in computing the FFT, etc. this can be some time before the sound
buffer is actually played, creating a "time parallax" between the
audio one hears and the spectrum and envelope displays.  I modified
playSound() to, when the audio monitor is open and audio from the
network is to be displayed, create a copy of the original sound
buffer with a WORD at the beginning giving its length, and hide
a pointer to it at the end of the WAVEHDR for the audio output.
When the buffer has completed playing, the MM_WOM_DONE message
handler in Frame.c passes this buffer to spectrumUpdate() and
then releases.  This does use more memory, but eliminates the
time parallax.  Note that the auxiliary sample buffer is allocated
only if actually needed; a NULL pointer indicates no auxiliary
buffer is attached to the WAVEHDR.

The envelope display in Spectral.c didn't account for the one-pixel
frame included in its client rectangle, resulting in the curious
flash of the leftmost line in the spectrum as it was painted and
then overwritten by the refresh of the frame.  Fixed.  The same
problem was present in the spectrum display, with less obvious
consequences; fixed there also.

I added computation of the maximum energy in a packet to energy()
in Spectral.c, and modified the envelope display so draw a red flag
at the edges of the envelope window for any packet containing a
sample which clipped.

Implemented fixes to Lpc/Lpc.c forwarded by Enzo Michelangeli.
These should eliminate distortion primarily due to overflows
in computing parameters.  LPC still doesn't sound great, but
it shouldn't break up so badly.  These fixes haven't yet been
integrated in the Unix version, but interoperate with un-fixed
LPC CODECs so there's no problem with Unix or earlier Windows
versions.

Cleaned up some Win16 legacy _fmemxxx calls in Speaker.c, as
well as some obsolete _huge and FAR declarations.

Sending .au format audio files did not update the audio monitor.  This
was due to the .au transmission code foolishly avoiding createSoundBuffer()
in the interest of "efficiency" (as if anything that ends up calling the
Windows API could be "efficient").  I ripped out the "roll-your-own
sound buffer" code and added logic to createSoundBuffer() in Connect.c and
spectrumUpdate() in Spectral.c to interpret an align argument of 0 to
mean that the sound buffer already contains mu-law samples at a rate
of 8000 per second.

Added radio buttons to the Audio Monitor dialogue to select whether
the maximum energy per packet (handy for setting input gain to
avoid clipping) or the RMS average is shown in the envelope panel.  The
clipping indicator is shown regardless of the setting of these buttons,
whose setting is saved in the application .INI file.

4 April 1999 -- John Walker

Cleaned up some obsolete FAR declarations in Netfone.h, Utility.c,
Dialogs.c, Loopback.c, Netfone.c, Rate.c, Xdsub.c.  Netfone.h is
now FAR-free.

Converted _fmemxxx calls in Loopback.c to memxxx.

Made declarations of CRC and modem open/close functions in Netfone.h
conditional on MODEM, marking them more clearly for elimination in
the future.

Fixed a couple of TRACE_FACE #ifdef/#endif pairs in Face.c which had
accidentally been indented.  This works OK in Monkey C but looks
ugly.

Release 7.0 beta 6.

5 April 1999 -- John Walker

RZG reported that the "t" at the end of the "Input" legend in the Text
Chat dialogue box was truncated.  This doesn't happen on my machine,
but it's due to Monkey C's moronic default of sizing static text boxes
to the precise size of their content on the machine on which they are
created.  I expanded the boxes containing both the "Input" and "Transcript"
legends to include adequate space for any reasonable font selection.

RZG also observed that since the common OPENFILE dialogue doesn't allow
you to enter a blank file name, there was no way to disable an outgoing
answering machine message other than editing the SPEAKFRE.INI file.  I
added "Clear" buttons for both the outgoing and incoming file names in the
Answer.c dialogue handler.

The answering machine displayed only the last two digits of the year
when showing the time and date of a message.  I modified it to use
ISO 8601 "yyyy-mm-dd" format.  The format used is Y10K, etc. compatible.

The "destest" project, not a part of Speak Freely but used to certify the
correct operation of the DES encryption used in Speak Freely (but not RTP
and VAT) protocol generated a number of warning messages at compile time
and failed to run because its test data file was not supplied on the
command line.  In addition, if you did manually run the program, it
reported errors because PERMUTATION was not defined when compiling Des.c,
which the test data require.  All fixed.  This has no impact on Speak
Freely whatsoever; it's just sweeping out an odd closet.

Release 7.0 beta 7.

April 18, 1999 -- Brian C. Wiles

  Added splash screen before main window is displayed.  It contains a
status line at the bottom in case the program hangs while starting.

April 29, 1999 -- Brian C. Wiles

  Started adding toolbar to main window.  The buttons only have text
for right now until I have some decent pictures for them.

May 1, 1999 -- Brian C. Wiles

  Changed toolbar to only be enabled by defining TOOLBAR.  The reason
for this is because Monkeysoft's operating system isn't properly
passing mouse messages such as WM_MOUSEMOVE and WM_LBUTTONDOWN to the
main MDI parent window.  The messages are, in fact, posted to the
MessageLoop() function, but disappear into Windows after that.  The
only thing I can figure out on that so far is that Windows doesn't,
by default, pass mouse messages to an MDI frame.  After all, why
on Earth (or whatever planet the Win32 developers are from) would
anyone ever want to do such a thing.  They're thinking is, "If *WE*
don't use that functionality, no one else will either."  This is
what makes Windows programming so much fun!

May 2, 1999 -- Brian C. Wiles
  *** Release 7.0 Beta 8 ***

  I finished testing Beta 8 with John Walker to make sure it still
works the way it should.

4 May 1999 -- John Walker

The "No Crypto" build failed because "comctl32.lib" was not included
in the link, causing the InitCommonControls() call in Init.c, added
to support the toolbar, to be undefined in the link.  I added comctl32.lib
to all configurations of the Speakfre project.

Since the toolbar is currently disabled, to avoid the risk somebody
may have a DLL conflict with the common controls DLL, which is only
used when TOOLBAR is defined, I disabled the call on InitCommonControls()
in Init.c when TOOLBAR is not defined.

Fixed FILEVERSION and PRODUCTVERSION in the Version resource to be
7,0,8,0.

May 7, 1999 -- Brian C. Wiles

  Designed a fancy logo for the Splash Screen with a telephone about
to plug into the Earth.  To do this, I gathered the following images:

	*  Starfield from John Walker's Terranova Screen Saver
	*  Colorized snapshot taken today from GMS-5 Satellite from URL:
	     http://rsd.gsfc.nasa.gov/goesg/earth/Weather/GMS-5/jpg/vis/4km/
	*  An RJ-45 wall jack.
	*  A CAT-5 Ethernet cable with connector on the end.
	*  A telephone.

  I then used the GNU Image Manipulation Program (The GIMP) to piece
together the images and create the glowing text.  I don't think it's
half bad for someone who's not a graphics artist.

May 8, 1999 -- Brian C. Wiles

  Finished new logo and added it to the splash screen.  The image is
600x400x256 resolution, designed to be large enough to see on high
resolutions while still viewable on a 640x480 (yuck) desktop.

May 9, 1999 -- Brian C. Wiles
  *** Release 7.0 ***

  Fixed ICQ detection bug where SetupICQ() would not update the
settings if Speak Freely was set as any other type of ICQ plugin
besides "File", such as "ClientServer" where the registry value would
be "Client Path" instead of "Path".  Thanks to "Hauke D." for
exporting his ICQ registry settings for me.

10 May 1999 -- John Walker

Splash_DlgProc in Dialogs.c generated a Bounds Checker squawk when it tried
to set the IDC_ABOUT_TITLE field in the splash dialogue box, which
existed only in the about dialogue box.  Since the information in the
IDC_ABOUT_TITLE is relevant, I added that field to the splash
dialogue box.

June 8, 1999 -- Brian C. Wiles
  *** Release 7.1 beta 1 ***

  Added IP address display to about dialog.  It will display "Unknown"
if the user is not connected to the Internet at the time.

  Fixed the splash dialog problems caused by the splash dialog having
the DS_SYSMODAL attribute.  This would hide initialization errors,
thus appearing to lock up.

  Added "Save Message" option to answering machine.  Just hit the button
after the desired message has been played, and choose a .WAV file to
write to.

June 9, 1999 -- John Walker

The Sflogo.bmp bitmap supplied with the 7.1 release was in 24 bit
per pixel mode which not only ballooned the size of the Speak Freely
executable by more than a factor of two, it would also fail to display
properly on machines with 256 colour displays.  I converted it to
a 256 colour bitmap like the one supplied with 7.0 which only
inflates Speakfre.exe by a factor of 50%.

The Connection Options dialogue failed to mention Blowfish among the
varieties of encryption available.  Fixed.

June 11, 1999 -- Brian C. Wiles

  Added "Save All" button to answering machine for saving the entire
message file to one .WAV file.

June 14, 1999 -- Brian C. Wiles

  Changed the behavior of answering machine to only play the current
message when the Play button is pressed.  This allows skipping to the
desired message number without having to play the messageses in
between.

June 15, 1999 -- Brian C. Wiles

  Changed IP address in About box to read-only edit field for copying
and pasting into connection window or another program.

June 29, 1999 -- Brian C. Wiles

  Added encryption mode to connection protocol line, and a software
line to display the other party's software name.

  Corrected mailing list subscription address to
imailsrv@speakfreely.org in the help file.

  Added Quick Start Guide to help file for first time users.

June 30, 1999 -- Brian C. Wiles
  *** Release 7.1 beta 2 ***

  Added indicator to show both the money saved on the current call, as
well as the total money saved since Speak Freely was installed.  This
should give the telco's something to chew on!  After all, why not show
the users just how much they would have been charged if they had
used their regular long distance carrier?  The dollar amount is based
on the United States' typical long distance rate of 10 cents per
minute.  Chances are you'll save more than that, but this is probably
the best example of the cheapest realistic long distance rate.

January 4, 2002 -- Brian C. Wiles
  *** Release 7.2 ***

  Fixed a GSM audio bug caused by using the same GSM handle for both
encoding and decoding audio.  This drastically improves the audio
quality of Speak Freely.  Most users will notice a decrease in pops
and clicks in the audio.

  Changed license to GNU GPL to ensure Speak Freely stays Open Source
and does not get hijacked by others in the future...at least not
legally, anyway.

  Removed the Money Saved indicator since it was confusing a lot of
people into thinking they were being billed for the calls they made.
You know, it's a sad world we live in where idiots can't figure out
what the word "saved" means, but, alas, this is not a perfect world.
