QRt is a QT window system port of Rt, Paul Lansky's mixing tool, which was originally designed for the NeXT platform, and subsequently ported to SGI and Linux. QRt will run under X86 Linux (under X Windows) and MacOSX (natively). It allows the user to play large numbers of different soundfiles, up to 64 at a time (depending on the CPU) as if they were notes, from a note list, with continuous control over amplitude, pitch, input and output skip, pan, and direction. QRt can be compiled to handle any number of (stereo) tracks. (For now, versions greater than 12 tracks still only have controls for 12 tracks on the second page, but up to 16 tracks can be controlled from the Track Map Window.)
The data is organized in terms of sounds and tracks. A track can only play one mono or stereo sound at a time. A segment of a sound played on a given track at a given time will be referred to as a note or playnote.
With this program you can:
Contains gain controls for each channel of the first 12 tracks and
the 2 output tracks as well as reports on peak amplitudes and mute
switches for each track.
A visual display and editor for soundfile segments on tracks, and a
moving cursor while playing mixes.

The path names of the relevant soundfiles are listed in the soundfile table. The number of the
soundfile is important and is used by playnote to reference
that soundfile. Soundfiles can be loaded by using the Load Input
Soundfiles command in the Soundfiles
menu, by dragging single or multiple sound icons onto the table, or by
dragging
sound icons into the track map window
(which creates a default playnote for the sound as well -- see below).
To load a single new soundfile at a specific location in the soundfile table,
right-click on the soundfile and choose Replace... from the popup
menu.
To insert a set of new soundfiles into the list, select the file you
wish to insert above, and choose Insert
soundfiles from the popup menu. If you want to load a
place holder at some point, choose Set
to 'null' from the same menu. The entire list may be
cleared via the Clear Soundfile Table
command in the Soundfiles menu.
Soundfiles can be auditioned directly from the soundfile table by simply double clicking on the name of the soundfile. Double click again to stop playback.
Soundfiles can be turned off and on either
during or before a mix is played by choosing Toggle on/off from the popup menu.
The gain column is editable. You can
change a gain by selecting and typing over, or inserting, or, you can
place the cursor in the gain and move the slider above the gain column
to change that gain. These gains control the amplitude of that
soundfile, and are multiplied by the gain factor on the playnote card,
the track gain for a given playnote and the output gain. To fine-tune a
gain you can click to the right and left of the slider indicator on the
soundfile gain slider.
The gain change will take place during a play with delay subject to
buffer size -- see lookahead buffers
-- if you change it with the slider. Otherwise it will only take effect
on the next play.
In the playnote editor you type in the actual commands for accessing and mixing the soundfiles. Each playnote command is usefully thought of as a note. It instructs the sound driver to play part or all of a soundfile at a given time, on a given track, with certain properties. You can copy and paste text into the playnote editor. The arguments can be placed in any order. Both the playnote editor and the large playnote editor use Emacs key bindings.
Each playnote command must appear on a separate line in the playnote editor. It needn't begin in the first column.
The only required argument for a playnote command is the the sound number (snd=x). All other arguments have default values if omitted.
Each note as specified by a playnote command has the following properties, specified with these tags:
If you have both a gliss() and transp=argument, then they will be added, i.e., each pitch value in the gliss() curve will have the transp= value added to it.
amp(0,.2,2,.5, | , 9, 1, 10, 0) means that this note will have a rise from .2 to .5 over the first 2 seconds, and have a decay from 1 to 0 over the last 1 second of the note, (assuming its duration is at least 3 seconds), and between these two times the amplitude will go from .5 to 1. If for example the note is 20 seconds long, this is equivalent to saying amp(0,.2,2,.5,19,1,20,0).
If there were not a stickpoint in the above example and the note were 20 seconds long, it would equivalent to saying amp(0,.2,4,.5,18,1,20,0). Here you can see that the times are warped relative to the actual duration of the note. There can be only one stickpoint in an argument set, but there can be any number of argument pairs around it.
playnote(snd=1,track=1,skip=2.5,at=3.5,amp(0,0,1,1,2,0),dur=4)
will cause snd 1 to be played on track 1, starting at 2.5 seconds into the sound, starting at time 3.5 in the mix, for 4 seconds with an envelope going from 0 to 1 and back down.
playnote(track=1,snd=1,transp=-2.5)
will simply play all of snd 1 on track 1 down 2.5 semitones.
The commands in the playnote
editor are initially loaded by hitting the load everything, or
reload
playnotes buttons in the control window. If some of the text in
the playnote editor is selected, only that portion will be
loaded. You can subsequently select a subset of the playnote commands
in the playnote editor to be added to the mix, or altered
within
the mix by simply selecting them with the cursor and hitting the reload
playnotes button. This will save a bit of time since it sometimes
takes a few seconds to load in a bunch of files and playnote commands.
Note that if a subsequently loaded playnote command occupies the same
time portion of a track as a previous one, then the first one will be
eliminated.
//this is a comment
playnote(track=1,snd=1) //this is another comment, but playnote is taken seriously
This window displays status and error messages. This window
displays reports on syntax errors from the playnote parse, and
indicates when sound and mix playback begins and ends. Note that
if a window other than the Main Window
is on top, syntax errors
are also displayed via a popup window.

It is not necessary to use this window, but it can provide some extra gain control. It is opened by clicking on the tab at the bottom of the main window. You can also mute tracks by clicking on the track number atop each set of sliders. The changes you make only go into effect a few seconds after you make the change, due to the large size of the intermediate sound buffers used to do the mix.
The track control window is quite useful for balancing sounds, and for preparing very complicated mixes which might exceed the throughput capacity of the machine. You can turn selected tracks off and on, balance them against each other etc.

The third window is simply a larger version of the playnote editor
on the main page. The purpose of this is to allow you to look at larger
groups of playnotes without having to scroll around. When you
toggle back and forth between this and the main window, each is
updated.
In many ways this is the most powerful
window in qrt. Here you
can see a visual representation of your
playnote commands, visually edit the start time, duration, track
number, and gain of any playnote, and add new playnotes via the
playnote text entry.
Once playnotes have been loaded with the load everything button, or with the reload playnotes button, this window will display a timeline layout of the tracks and soundfile segments on each track. Below each soundfile are two numbers indicating starting time and duration for that segment. While playing, a cursor will sweep across the screen, and the time will be displayed in the lower left hand text field. If you click the left mouse button anywhere in the window, the next play will begin at the time you clicked and the starting time will be displayed in the right of the two text fields. (You can also simply type a number in that window to force a starting time.) Remember that if you are skipping into a mix, there may be a pause before it starts to play. If you click the left mouse button while a sound is playing it will stop playing. While playing, if you click the right mouse button anywhere in the window, the mix will start to play at this point. If it is already playing it will stop and start again at the point at which you clicked. This enables you to jump around a mix. Again, there may be some slight delay, depending on your timescale, and the size of the timeskip.
On the lower right hand side of the window there is an autoscroll switch. Toggling this on will cause the window to scroll to a location during playback in which the moving cursor is always visible.
The zoom in and zoom out buttons will give you differently scaled displays. When you are zoomed out the smallest visible duration may be about 1/4 inch wide. In the zoom out case the time and duration of each note will not be displayed. Holding down the <shift> key while using these buttons will zoom you in and out by 50% (rather than 10%).
If you have a comment after a playnote: e.g.
playnote(track=1,snd=2,at=23,skip=23)//big noise
this comment will appear above the bar representing the playnote. If
there is no comment, the soundfile name will appear, stripped of its
full path. In all cases, the text is preceded by the sound
number in brackets. For example, the playnote listed above would
have the text "[2] big noise". Sometimes, depending on the length
of your comments and filenames, and the length (duration) of the
playnote, the end of the text will be truncated (clipped). The comments
are always clipped to the length of the playnote's visible
rectangle. Zooming in the display usually fixes this, or reducing
the size of the font used to display text in the track map window (see
below). There are also cases when the end of one playnote will
overlap the beginning of another. These should be obvious, and can be
checked using the Show Note Overlaps
menu command.
The font for the text in the track map window can be set in the resource file using "trackMapFont", and set via the Set Editor Font menu command. The names need to match the QT font specification.
The shape of the bars in the track map window reflects the amplitude modifications you have made in the playnotes with the amp() command, with the apparent height of the bar representing its amplitude relative to the maximum specified by the time, amp pairs.
The color of the bars reflects the gain as stated in the gain=
command, as modified by the input soundfile gain and the track gain
from the Track Control Window.
Using the default color scheme, bright tan is light and dark brown is
soft, with various shades in between reflecting gains in between. This
color scale can be modified by editing the resource file (see Customizing).
Several attributes of a playnote can be easily changed from this window. If you shift-click the left mouse button on any playnote bar, the playnote script for that note will appear in the textfield below and the soundfile name and number in the lower field to the right. You can then edit it. When you press the reload playnotes or reload everything button the changes will take effect. If you want to make a bunch of changes in several playnotes you can enter the changes into memory by just editing them all, then hitting either of the two reload buttons to make all the changes at once.
You can play the playnote or the soundfile by itself with the play buttons to the left of each field. In the case of the playnote, what is happening is that you are playing the mix with all the other tracks turned off, and skip and end set to that playnote's time. Note that if you have a small timescale, and a lot of tracks and sound, there may be a delay before it starts to play. (Skips are much faster with larger timescales.). The playnote playback can be stopped using the stop button; the soundfile playback is stopped by pressing the play button again (note that its text had changed to "stop"). You can also audition the playnote by pressing the <space> bar, and stop it by pressing it again. If you hold down the shift key while clicking on the play button, the note will start to play at the time specified in the skip field, rather than at the beginning of the note. This way you can play selected portions of a playnote as well.
There are several causes of potential confusion when using this page. First, the reload playnotes button may not erase old playnotes from memory, but they may not appear on the map. If, for example, you change tracks, the playnote may still live on the old track as well but will not appear on the map (unless it previously overlapped the end of an earlier playnote, in which case it will not sound). The only way to make sure that your map is accurate is to hit the reload everything button. This is not necessary, however, if you are just changing amplitude characteristics, or making the starting time a bit earlier.
If, on the main page, you loaded the playnotes after having made a selection in the playnote editor, what you will see is the result of that selection. Changes in any playnotes will, of course, be copied back to the playnote script, whether or not a selection is in effect.
Another potential source of confusion is in the case where you want to simply reload one playnote in the main window by selecting that playnote and hitting reload playnotes. In this case only that playnote will appear in the track map, although all the other playnotes selected previously will still sound. (This should be fixable, but my brain hurts right now.)
This window makes it very easy to fine tune characteristics of a mix. You can mute and/or solo tracks with the buttons to the left of each track, and start at any time in the mix simply by clicking the left mouse button at that time, and then make changes in the playnotes and try again. When a track is muted, all displayed playnotes for that track will appear partially greyed-out as a reminder.
While in the Track Map Window, if you ask to edit a soundfile
(Ctrl-E, or Edit Input File, from the Soundfile menu) the
editor
will open the soundfile which is currently listed in the soundfile
textfield on the lower right of the window.
A potential source of confusion exists when you have used the offset
and/or the addoffset feature
in the playnote editor. Remember
that when you enter new playnotes the starting time will be incremented
to whatever the cumulative offset is at that point for the mix, and the
displayed playnotes will reflect this.
Playnotes can be moved around with the mouse or the arrow keys. To move a playnote with the mouse, hold down the <shift> key and click on the playnote you want to move with the left mouse button. Note that the text and end-bars for this note are now displayed in red. This indicates that this is the playnote selected for editing. Now while holding the <shift> key down, drag the playnote with the mouse. You will see a white version of the soundfile move with the mouse. You can change both its start time and track number this way. As you move the soundfile you will see the values for at= and track= change in the playnote editor below. When you release the mouse button, the original image of the soundfile will disappear and the new one will assume its color. If the "autoReload" option is set (via resource or the Auto-Update track map command in the Options menu), the playnote list will automatically reload each time you release the mouse button, and the playnote will be unselected automatically. If the option is not set, you can move around any number of soundfiles before hitting the reload playnotes or load everything button to put the changes into effect. In either case, if you move soundfiles to the right, later in time, you should use the load everything button.
The duration of playnotes can also be adjusted with the mouse. To do this, hold down the <shift> key and click on the playnote with the right mouse button. Dragging to the right and left will adjust the duration of the playnote. As you do this, the value for dur= changes in the playnote editor below. Note that you cannot drag between tracks using this command. If the "autoReload" option is not set, you will have to reload playnotes after you are done (load everything would never be needed for this operation).You can also move playnote by using the arrow keys, which will move them up or down one track at a time or back and forth in time, in 0.1 second increments. This feature merely requires that you have shift-clicked on the playnote on the playnote you wish to move with the left mouse button. When you are done, hit <return>, or shift-left-click anywhere in the window to redraw the playnote in its new location. Again, if the "autoReload" option is not set, you have to reload playnotes or load everything after you are done.
The gain of playnotes may be adjusted up and down with the keypad
'+' and '-' keys or the mouse wheel (if you have one). To use the
keys, select a playnote using <shift>-leftmouse. The '+'
key will increase the gain by 1 dB; if the <shift> key is
down as well, the gain will increase by 3 dB. The '-' key will
reduce the gain in the same fashion. To use the wheel, simply
scroll the wheel up and down. As you do this, you will see the
values for gain= change in the playnote editor below.
Note
that the relative colors of the various playnotes will also change to
reflect the new gain relationships. Note: You must
reload playnotes after altering gains to hear the effect -- these do
not
auto-reload.
Playnotes can be copied and pasted (one at a time) by selecting them
and using the Copy and Paste commands from the Edit menu or the
keyboard equivalent. When pasting, the new playnote will appear
in the next higher track (subject to wrapping), and its text will be
visible in the playnote editor. Paste will not check for overlaps
with existing playnotes, so you will need to do this after the paste.
Playnotes may be deleted by hitting the <delete> or <backspace> key after selecting them. The playnote entries don't actually go away; a comment is placed before the playnote line in the score. This allows you to undelete the entry at some future time, or clean them up (remove completely) on your own, later. The playnote list is always updated after a delete.
Tracks can be muted and soloed directly from the track map window by
using a combination of the middle mouse button and various keyboard
letters while the mouse is over a track. Use the z key to mute tracks, the x key to unmute tracks, the s key to solo tracks, and the u key to unsolo tracks. You can drag
the mouse up and down to select groups of tracks. When
a track is muted, the line under that track will turn blue, and
all playnotes on that track will appear partially grayed-out.When
soloed, the line under that track will turn yellow, and playnotes on
all unsoloed tracks with appear partially grayed-out. You can mute and
unmute all tracks with Ctrl+t,
and unsolo all tracks with Ctrl+U.
If you make any changes in the names, conditions, or numbers of
soundfiles, or the timescale or maximum number of tracks, you
will have to restart the driver (kill everything, load everything).
Most other changes, such as altering the times of a mix in the track
control
window, some changes in the playnote editor, turning
soundfiles off and on in the soundfile table, making any
changes in the tracks window, can be done without restarting
the driver.
The entire state of a mix can be saved to a simple ascii file with
the suffix .qrt. This is done from the File menu,
and if it is a new mix, you will be prompted for a filename. Similarly,
you can restore a mix by starting qrt with the script specified in the
command line, or by opening qrt and then loading in the script from the
files menu.
When you finally want to save a mix to disk, before hitting the play button, choose an option from the Play Mix To menu in the Main Window. If you choose one of the three file options, qrt will prompt you for a soundfile path. Now hit the play button. What will be written to disk is exactly what you would have heard had you hit the play button alone. As the mix proceeds, you will be updated on its progress via a dialog panel, and you have the option of interrupting the mix at any point. The output option resets after each play command, so you will have to repeat the above steps to rewrite the mix to disk.
It's up to you to make sure you have enough disk space.
Qrt errors fall into one of three classes: setup errors, soundfile errors and score errors. Setup errors involve parameters that are parsed prior to the playnote list, and include things like track count, track gains and volumes, and timescale settings. Soundfile errors include nonexistent or malformed soundfile path names and invalid or unsupported sound formats. Score errors comprise the majority of the possible errors, and include invalid command strings, invalid parameter values, malformed parameter lists (e.g., odd number of parameters passed to amp()), and any other place where the parser gets confused.
No error checking is done until you first attempt to load everything, at which time qrt will report all three types of errors. Opening and closing qrt scorefiles without loading does not invoke the parser. If there is a setup error during loading, qrt will report it in the status window on the main panel, and via a pop-up alert if you are currently viewing any other panel.
If setup succeeds, but there is an error involving one or more of the soundfiles, qrt will report it in the same manner, with these additional features: A pop-up with an OK/CANCEL choice will appear, and the offending soundfile's row will be selected. This should make it easier to locate the problem file. If you choose not to cancel upon receiving the pop-up, qrt will continue to parse the soundfile list until it either reaches the end or hits another error -- but it will not continue beyond that point. If you choose cancel, the parse will stop at that point.
If setup and soundfile loading both succeed, but there is an error in your score, in addition to the pop-up and status display, the offending line will be highlighted in the playnote editor. If the driver was already loaded and the error occurred during the score parse, you can just hit reload playnotesafter fixing the error. You will at least need to unselect the highlighted line before reloading or else you will only load that one line!.
With the exception of the soundfile errors, qrt stops parsing at the first error it encounters. You will have to reload and fix later errors one at a time.
To edit an input soundfile, right click anywhere on that soundfile's line in the soundfile table, except over its gain, and choose edit with external editor from the context menu. The sound editor program specified by the soundEditor resource will be invoked on the soundfile, if the program is installed on your machine. Otherwise you will be alerted that it can't find it. Under Linux, the current default is Douglas Scott's "mxv" program.
You can also edit the sound output of a playnote, as modified by its
parameters. To do
this you need to select a playnote in the Track Map Window by
shift-leftmouse clicking on it. The playnote will then appear in the
form at the bottom left of the Track Map Window. Then, Ctrl-K
will write that playnote's sound in /tmp, and the sound editor program
will open that file. When you are done you will be prompted to see
whether or not you want to save the soundfiles in /tmp. Note that any
changes made to the playnote soundfile with the sound editor will have
no effect on the resulting mix.
Qrt works by dividing time
into ticks, as specified in the timescale
window. Each tick contains information about what is going on at that
track, at that time. For each track there are 200000 ticks allocated
for each cell of 10 parameters. Normally only a small number of these
cells are used, but when the memory is allocated the operating
system checks to see if that much virtual memory is available. So, for
12 tracks, you would have to have 12*36*200000 = ~87
megabytes of virtual memory. In the (unlikely) event that you had all
12 tracks going for all 200000 ticks and were setting all parameters
all the time you *might* start to use up your swap file. (I've never
seen this happen, but it's possible). The following are recommendations
regarding use: First, when the 'load everything' button is in its
yellow state, no virtual memory is allocated. This means that you can
have lots of different rt jobs running at the same time, as long as the
sum of the requested virtual memory doesn't exceed what you have
available. Second, use only as many tracks as you need, and specify the
limit in the tracks window. This way less virtual memory will be
requested. Finally, if you are going to 'minimize' an rt job, be sure
to 'kill everything' before you do. This way it will take up only a few
MB of available memory.
Look
ahead buffers:. There are two
internal buffers which are used. One is in the driver, and the other is
in the audio hardware. The driver buffer is fixed, but the size
of the audio
buffers can be adjusted to be between one and six 32k buffers by using
the slider on the track window. The effect of decreasing the number of
these buffers is that changes made by moving the sliders for the
individual tracks and the output tracks will take effect more quickly,
as will the stop and pause buttons. The negative effect is that there
may be glitches since the audio hardware may run out of data. For
complicated
mixes it is best to use a larger number of buffers. You don't need to
restart the driver to reset the buffers, but they will not take effect
during a play.
You have three soundfiles you want to work with. Let's call them talk, whistle and knock.
Most of the visible widgets in qrt
have tooltip text. This will display as a text popup when the
mouse hovers over a widget. This can be turned on and off from
the Help menu.
As of April 2005, QT's internal customization via resource system is
nonexistent. A small number of features and settings are
currently supported via a qrt initialization file, which is called .qrtrc under Linux, .qrtinit under MacOSX, and qrt.ini under Windows (not supported
yet):
Using an ungainly customized system I created, attributes for
font, foreground color, and background color may be set of any of the
below-listed widgets using the X resource format. For example, to
set the _outputGainLabel foreground color to blue, add a line to your
qrt initialization file as follows:
_outputGainLable.fg:
Blue
To set the background for all push buttons white, you can specify
the widget type instead:
QPushButton.fg: White
In the lists which follow, the name in bold is the class name; the
_underbarName is the name of the specific widget. Please
note that I will not guarantee that this set of widgets and/or the
widget names will remain consistant or complete over the course of
development. I will do my best to keep this list
updated.
QCheckBox _autoScrollCheckBox,
_decibelButton
QComboBox _outputChooserComboBox
QLabel _L_Label,
_lookAheadBuffersLLabel, _muteLabel, _outputChooserLabel,
_outputGainLabel, _pixmapLabel1, _playFromLabel, _playFromLabel2,
_playFromLabel3, _playToLabel, _playToLabel2, _playToLabel3,
_QRTLabel1, _R_Label, _soloLabel, _soundfileGainLabel, _timeScaleLabel,
_trackControlFormLabel, _tracksLabel
QLCDNumber _currentTime,
_currentTime2, _currentTime3, _currentTime4
QLineEdit _endTimeEdit
_endTimeEdit2, _endTimeEdit3, _lapTime2,
_leftLevel1, _leftLevel10, _leftLevel11, _leftLevel12, _leftLevel2,
_leftLevel3, _leftLevel4, _leftLevel5, _leftLevel6, _leftLevel7,
_leftLevel8, _leftLevel9, _leftOutputLevel, _leftPeak, _leftPeakHold,
_rightLevel1, _rightLevel10, _rightLevel11, _rightLevel12,
_rightLevel2, _rightLevel3, _rightLevel4, _rightLevel5, _rightLevel6,
_rightLevel7, _rightLevel8, _rightLevel9, _rightOutputLevel,
_rightPeak, _rightPeakHold, _selectedPlaynoteEdit,
_selectedPlaynoteSoundfile, _soundfileGain, _startTimeEdit,
_startTimeEdit2, _startTimeEdit3, _startTimeEdit4, _timeScaleEdit
QMenuBar _menubar
QPopupMenu _controlsMenu,
_editMenu, _fileMenu, _helpMenu, _optionsMenu, _playnotesMenu,
_soundfilesMenu, _windowMenu,
QPushButton _killDriverButton,
_killDriverButton2, _killDriverButton3, _killDriverButton4, _lapButton,
_lapButton2, _loadDriverButton, _loadDriverButton2, _loadDriverButton3,
_loadDriverButton4, _loadPlaynotesButton, _loadPlaynotesButton3,
_loadPlaynotesButton4, _mainTrackMuteButton1, _muteButton1,
_muteButton10, _muteButton11, _muteButton12, _muteButton2,
_muteButton3, _muteButton4, _muteButton5, _muteButton6, _muteButton7,
_muteButton8, _muteButton9, _pauseButton, _pauseButton2, _pauseButton3,
_pauseButton4, _playButton, _playButton2, _playButton3, _playButton4,
_playSelectedInputSoundfileButton, _playSelectedPlaynoteButton,
_resetButton, _rewindButton, _stopButton, _stopButton2, _stopButton3,
_stopButton4, _trackMuteButton1, _trackSoloButton1, _zoomInButton,
_zoomOutButton
QRadioButton _checkPeakButton
QSlider _leftOutputSlider,
_leftSlider1, _leftSlider10, _leftSlider11, _leftSlider12,
_leftSlider2, _leftSlider3, _leftSlider4, _leftSlider5, _leftSlider6,
_leftSlider7, _leftSlider8, _leftSlider9, _rightOutputSlider,
_rightSlider1, _rightSlider10, _rightSlider11, _rightSlider12,
_rightSlider2, _rightSlider3, _rightSlider4, _rightSlider5,
_rightSlider6, _rightSlider7, _rightSlider8, _rightSlider9,
_soundfileGainSlider
QSpinBox
_lookAheadBuffersSpinBox, _trackCountSpinBox
QTable _soundfileTable
QTextEdit _playnoteEditor,
_playnoteEditor2, _statusText
Thanks go profusely to Paul Lansky for creating the original version of this program on the NeXT platform, for his port to the SGI platform, his initial work porting Rt to Linux, and his advice while porting to QT and adding new features. The following are Paul's original acknowledgments for his ports, I keep them here because we all stand on the shoulders of our predecessors:
Thanks to David Jaffe for important additions to the NeXT port, which will also make it into this port, eventually. Peter Velikonja did the major work in de-NeXTifying the sound driver so that it would work without the SoundKit. To Doug Scott for simplifying the use of the SGI Audio Library for me. Thanks to Bill Schottstaedt for helping to grok aiff files. And, most of all to Kent Dickey, whose class project in Cos/Mus 325, the driver for this app, has helped out so many people since that time. (I should have given you an A+)
Paul Lansky 1/5/97
Douglas Scott
October, 2005