TiogaVoicePrivate.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Swinehart, April 8, 1987 11:51:50 am PDT
Polle Zellweger (PTZ) July 21, 1987 12:11:31 pm PDT
TiogaVoicePackage is entirely user interface at this time. If any of its facilities sprout external client interfaces, they should move to a TiogaVoice interface.
DIRECTORY
ImagerFont USING [ Font ],
Jukebox USING [bytesPerChirp],
MBQueue USING [Queue],
Menus USING [Menu, MenuProc, MenuEntry],
Rope USING [ROPE],
TEditDocument USING [SelectionId],
TiogaOpsDefs USING [Location, Ref, WhichSelection],
ViewerClasses USING [Viewer],
ViewerEvents USING [EventRegistration],
VoiceRope USING [Handle, IntervalSpecs, RequestID, VoiceRopeInterval]
;
TiogaVoicePrivate: CEDAR DEFINITIONS = BEGIN
Types and values
VoiceViewers
VoiceViewerInfoList: TYPE = LIST OF VoiceViewerInfo;
VoiceViewerInfo: TYPE = REF VoiceViewerInfoRec;
VoiceViewerInfoRec: TYPE = RECORD [ -- the data structure underpinning all voice viewers
viewer: ViewerClasses.Viewer,
viewerNumber: INT,
ropeInterval: VoiceRopeInterval, -- the rope interval represented by the viewer
soundList: SoundList, -- a list of the sound/silence intervals in that rope interval
remnant: INT, -- the display shows the rope interval using characters representing a fixed amount of time: this is samplesInRope MOD lengthOfACharacterInSamples
charMarkList: LIST OF INTNIL, -- an ordered list of the characters which have character marks on them
textMarkList: LIST OF TextMarkEntry ← NIL, -- see below: list is again ordered
ageList: LIST OF IntPair ← NIL, -- see below: each age lasts until the next element in the list, otherwise until the end of the viewer
color: BOOLEANFALSE, -- is the viewer currently on the color display?
edited: BOOLEANFALSE, -- the true indication whether the voice has been edited: at times may not agree with the viewer's 'edited' flags
editInProgress: BOOLEANTRUE, -- set if an initiated edit has yet to complete: this includes all cut/paste operations; input to the viewer; alteration of markers and redrawing operations, since none of these is allowed to occur simultaneously in this implementation. It is created TRUE and only set FALSE once the viewer's contents are valid
destroyEvent: ViewerEvents.EventRegistration,
changeColumnEvent: ViewerEvents.EventRegistration,
parentViewer: ViewerClasses.Viewer ← NIL, -- trace of where the SourceMarker for the viewer is, NIL if no parent
positionInParent: TiogaOpsDefs.Location -- ditto: invalid if parentViewer = NIL. HINT ONLY!
];
Textual markers are held as a list of the following elements as part of the VoiceViewerInfo; see VoiceMarkersImpl and TextInVoiceImpl for more details
TextMarkEntry: TYPE = REF TextMarkRec;
TextMarkRec: TYPE = RECORD [
position: INT, -- the number of the character to which the text is attached
text: Rope.ROPE, -- the text itself
displayChars: INT, -- how much of the text can be displayed [without running into the next TextMarkEntry], in characters
width: INT -- how much space this display will take, measured in voice character widths
];
aging markers in VoiceViewerInfo are held as a list the following records
IntPair: TYPE = RECORD [
position: INT,
age: INT
];
These constants define how a voice rope maps onto a graphical representation (in fact a rope of characters in special font, collectively producing the capillary display.)
soundRopeCharsPerSecond: NAT = 4;
soundRopeCharLength: NAT = Jukebox.bytesPerChirp/soundRopeCharsPerSecond;
Jukebox.BytesPerChirp is the measure of voice samples per second
soundRopeCharDivisions: NAT = 4; -- number of stripes per character;
soundRopeResolution: NAT = soundRopeCharLength/soundRopeCharDivisions;
voiceViewerMenu: Menus.Menu;
The menu for all voice viewers
voiceViewerInfoList: VoiceViewerInfo; -- now private to VoiceViewersImpl
The list of all currently existing voice edit windows.
Data structures used in editing voice.
VoiceRopeInterval: TYPE = VoiceRope.VoiceRopeInterval;
RECORD [ropeID: Rope.ROPE, start: INT, length: INT]
Selection: TYPE = REF SelectionRec;
SelectionRec: TYPE = RECORD [
Specification of where to insert voice into a slab: if ropeInterval.length # nil then selection is to be deleted first.
viewer: ViewerClasses.Viewer,
voiceViewerInfo: VoiceViewerInfo,
ropeInterval: VoiceRopeInterval,
displayNode: TiogaOpsDefs.Ref
];
The purpose of the next data object is to keep track of voice which is to be moved to some part of some slab—both a VoiceRopeInterval and a cache of the SoundList, charMarkList and textMarkList which correspond to it.
SoundInterval: TYPE = REF SoundIntervalRec;
SoundIntervalRec: TYPE = RECORD [
ropeInterval: VoiceRopeInterval,
soundList: SoundList ← NIL,
charMarkList: LIST OF INTNIL,
textMarkList: LIST OF TextMarkEntry ← NIL
];
SoundList: TYPE = LIST OF Sound;
Sound: TYPE = RECORD [
silence: INT, -- a Sound element simply consists of a length of silence
sound: INT -- Followed by a length of sound - either may legally be zero
];
Playback
SelectionsAfterRedraw: TYPE = { unAltered, deSelected, primaryOnInsert };
PlayBackRequestState: TYPE = {waiting, busy, done};
VoiceMarkers
Constants describing the size of characters used in the voiceProfile/voicePlay fonts
These probably should be extracted from font parameters. Note that when there is artwork on any part of a line, selections on that line will be as tall as the artwork (see TextInVoiceImpl for how the ascent of the artwork is set). If there is no artwork on that line we still want the selections to be 'tall', so the two voice fonts each contain one character ('?) which has an ascent of 24.0 - the same as the artwork. This causes all selections to be 'tall'.
See also comments in TextInVoiceImpl about the interaction of artworks and VoiceProfile.Style
voiceCharWidth: REAL = 8.0;
voiceCharAscent: REAL = 9.0;
voiceCharDescent: REAL = 1.0;
voiceCharHeight: REAL = voiceCharAscent + voiceCharDescent;
voiceMarkerFont: ImagerFont.Font; -- the font used to display the text
voiceCharSet: BYTE = 0; -- since at times a 16-bit Xerox character code is called for
VoiceInText
VoiceWindowRef: TYPE = REF VoiceWindowRec;
VoiceWindowRec: TYPE = RECORD [
This record makes voice window properties volatile (i.e. they don't get preserved when the file containing them is saved/stored
label: Rope.ROPE
];
thrushHandle: VoiceRope.Handle;
voiceButtonQueue: MBQueue.Queue;
VoiceAging
oldest: READONLY INT;
youngest: READONLY INT;
Procedures
VoiceViewers
Routines to create and delete voice viewers, data structures used in editing them and a monitor routine for locking a viewer against simultaneous attempts to manipulate it
routines to create and delete voice viewers.
BuildVoiceViewer: PROC [
voiceID: Rope.ROPE, textInVoice: Rope.ROPE, youngVoice: BOOLEAN]
RETURNS [
viewer: ViewerClasses.Viewer, viewerInfo: VoiceViewerInfo, viewerNumber: INT];
Build a new voice viewer. If voiceID = NIL then leave it with voiceVewerInfo.editInProgress TRUE (used when opening a dictation machine viewer), otherwise call SetViewerContents, passing on all three parameters.
SetViewerContents: PROC [viewer: ViewerClasses.Viewer, viewerInfo: VoiceViewerInfo, voiceID: Rope.ROPE, textInVoice: Rope.ROPE, youngVoice: BOOLEAN];
Set the contents of the viewer to the given voiceRope (voiceID) with the given textual annotations on it (textInVoice) and then make it editable. If youngVoice then set it to youngest edit and age all other voice (see Voice Aging section), otherwise create it at maximum age and do not do any aging of other voice.
SetTiogaViewerParams: PROC [viewer: ViewerClasses.Viewer, newcontents: Rope.ROPE] RETURNS [voiceNode: TiogaOpsDefs.Ref];
Set the contents, style, and looks for a voice viewer.
SetVoiceLooks: PROC [node: TiogaOpsDefs.Ref, start, len: INT, looks: Rope.ROPE];
Used to set looks to provide voice playback feedback.
SetParentViewer: PROC [viewerInfo: VoiceViewerInfo, parentViewer: ViewerClasses.Viewer, positionInParent: TiogaOpsDefs.Location];
RemoveParentViewer: PROC [viewerInfo: VoiceViewerInfo];
Called when the referent to the voice viewer is about to be deleted.
MakeVoiceEdited: PROC [viewer: ViewerClasses.Viewer];
Marks a voice viewer as edited: viewer, viewerInfo, and icon.
SetVoiceViewerEditStatus: PROC [viewer: ViewerClasses.Viewer];
Brings viewer into agreement with viewerInfo about whether the viewer has been edited.
FindAttachedVoiceViewers: PROC [viewer: ViewerClasses.Viewer] RETURNS [viewerInfoList: VoiceViewerInfoList];
Returns a list of voiceViewerInfo describing all voice viewers whose parent is the viewer.
RemoveParentPointersTo: PROC [viewer: ViewerClasses.Viewer, findSplits: BOOLEAN];
Removes the parentViewer pointers for all voice viewers pointing to this viewer. If findSplits is TRUE, will replace with a pointer to a split of the viewer if one exists.
GetVoiceViewerInfo: PROC [n: INT] RETURNS [viewerInfo: VoiceViewerInfo];
Given the viewer number, returns the viewerInfo for the associated voice viewer.
GetVoiceViewerInfoList: PROC RETURNS [vList: VoiceViewerInfoList];
Returns the current value of VoiceViewersImpl.voiceViewerInfoList.
GetVoiceLock: PROC [VoiceViewerInfo] RETURNS [BOOLEAN];
A monitor to prevent more than one manipulation on a voice viewer at a time. Does NOT include playback which is separately monitored. Since VoiceViewerInfo is not an opaque type, dropping a lock is the right of anybody who already holds it and thus is not monitored (belch).
Playback
Coordinates playback queues and corresponding visual cue queues. Events that complicate this are requests to play voice not in a slab (i.e. not wanting visual cues), requests to stop playback, and editing a slab while it is being tracked. All these events MUST be handled by these procedures. That ought to include both Finch and WalnutVoice eventually—the correct procedures are provided by this interface to both play a complete rope and stop the playing of all ropes.
PlayBackMenuProc: Menus.MenuProc;
The routine behind the PlayVoice button for both text and voice viewers.
PlaySlabSection: PROC [
viewer: ViewerClasses.Viewer, node: TiogaOpsDefs.Ref, from, to: INT];
'node' is the voice-profile rope in the voice viewer 'viewer'. Play all the sound represented by characters 'from' through 'to' inclusive.
PlayWholeSlab: PROC [viewer: ViewerClasses.Viewer];
PlayRopeWithoutCue: PROC [voiceID: Rope.ROPE];
Given a tune which is not pictorially represented, just play it.
PlayBackInProgress: PROC RETURNS [BOOLEAN];
Tests whether the playback system is active: regard it as a hint and call the next procedure before starting to record.
CancelPlayBack: PROC;
Aborts any playback cues in progress and any playback requests queued up: doesn't call voicerope.stop.
GetCurrentPlayBackPos: PROC RETURNS [display: BOOLEANFALSE, viewer: ViewerClasses.Viewer←NIL, currentPos: INT←-1];
SetPlayBackState: PROC [reqID: VoiceRope.RequestID, newState: PlayBackRequestState];
Respond to playback progress reports from Finch/Thrush.
RemoveViewerReferences: PROC [viewer: ViewerClasses.Viewer]
RETURNS [okay: BOOLEAN];
Changes all queued visual playback entries for a viewer to non-visual ones, unless
the viewer is currently at the head of the cue queue as a visual entry, in which case it does nothing and returns false. Must be called before a viewer is destroyed: if ~okay then viewer must not be destroyed.
RedrawViewer: PROC [
viewer: ViewerClasses.Viewer, newContents: Rope.ROPE,
unchangedHead, deleteChars, insertChars: INT, timeRemnant: INT, age: BOOLEAN,
selectionsAfterRedraw: SelectionsAfterRedraw]
RETURNS [newNode: TiogaOpsDefs.Ref];
Called in order to redraw an edited voice viewer, which may or may not be in the queue of viewers in which cues are appearing or are about to appear. newContents is the result of the edit as is timeRemnant, and the three INT arguments refer to the display prior to the edit.
All voice viewers will be 'aged' by this call (see the VoiceAging functions for details) unless age=FALSE or insertchars = 0.
Because the contents of the viewer are to be completely reset, any selections therein will be upset. How selections are to be affected must always be specified explicitly. The choices (only concerned with primary and secondary: feedback, should it be there, will always be destroyed) are enumerated in SelectionsAfterRedraw.
Record
Adds voice to both voice and text viewers.
RecordingInProgress: PROC RETURNS [BOOLEAN];
Tests whether recording system is active: n.b. should only be regarded as a hint and StopRecording should be called before beginning anything that needs to be synchronized. However, since in this system recording and playback requests will be serialized using mouse button queues, this procedure is regarded as the truth.
AddVoiceProc: Menus.MenuProc;
The procedure behind all AddVoice buttons.
StopRecording: PROC;
Should be called after VoiceRope.Stop has been called, to halt the recording mechanism.
DictationMachine: Menus.MenuProc;
The procedure behind all DictationMachine buttons.
RecordInPlaceOfSelection: PROC [selection: Selection];
Called by Dictation Ops when a selection (possibly zero length, in which case don't delete it) is to be replaced by new voice input. The selection is assumed to be locked with GetVoiceLock and there is assumed to be no playback in progress at the time of the call.
SoundList
Sound lists are descriptions of the silence/sound profiles of a ropeInterval and form part of the data structure behind a voice viewer
SoundListFromIntervalSpecs: PROC [
intervalSpecs: VoiceRope.IntervalSpecs, lengthOfRopeInterval: INT]
RETURNS [SoundList];
ExtractSoundList: PROC [voiceViewerInfo: VoiceViewerInfo, soundInterval: SoundInterval];
voiceViewerInfo contains the sound list for a complete rope. Use the interval specification from soundInterval.ropeInterval to create a new sound list for that interval, placing it in soundInterval.soundList
ReplaceSoundList: PROC [
voiceViewerInfo: VoiceViewerInfo, cutStart: INT, cutLength: INT, replacement: SoundList];
voiceViewerInfo contains the sound list for a complete rope. Cut out the specified portion and at the cutting point insert the replacement soundlist.
SoundChars: PROC [
viewerInfo: VoiceViewerInfo, skipChars: INT ← 0]
RETURNS [soundRope: Rope.ROPENIL, remnant: INT];
Produce the graphical representation of the SoundList within a VoiceViewerInfo record. The caller already has the first skipChars of the representation in his hand, so omit these from the returned rope. If the sound list does not exactly fill a whole number of characters, return as remnant the number of samples left over. Any marker characters indicated in the VoiceViewerInfo are inserted

LastSilenceInSoundList: PROC [soundList: SoundList, lengthGreaterThan: INT ← 0]
RETURNS [startsAt: INT, lasts: INT ← -1];
All INTs are values in samples, as for the rest of this interface. lasts=-1 indicates that there are no silence intervals in the list of lengthGreaterThan that specified
VoiceEditOps
The editing primitives supported (and intercepted for rejection) for voice viewers.
Mumble: PROC [opAttempted: Rope.ROPE, viewerTypes: Rope.ROPE];
Called when an op is attempted on a combination of viewer types for which it is invalid.
DescribeSelection: PROC [
which: TiogaOpsDefs.WhichSelection, forceDelete: BOOLEAN,
returnSoundInterval: BOOLEAN, viewerAlreadyLocked: ViewerClasses.Viewer ← NIL]
RETURNS [failed: BOOLEAN, selection: Selection, soundInterval: SoundInterval ← NIL];
This routine will characterize the voice corresponding to a selection. If the selection is pendingDelete (or if forceDelete) then the selection.ropeInterval will have non-zero length; if returnSoundInterval then the sound interval corresponding to the whole selection will be characterized—to be saved and inserted somewhere else.
Selections can only be described if the voiceLock can be taken out on them. This routine returns failed if it cannot get the lock; otherwise it returns with the lock still held. However the caller may pass a viewer in viewerAlreadyLocked to say that he already has that one locked and if this selection is in the same viewer then we may proceed
ReplaceSelectionWithSavedInterval: PROC [
selection: Selection, soundInterval: SoundInterval, leaveSelected: BOOLEAN, destroyOK: BOOLTRUE]
RETURNS [viewerDeleted: BOOLEAN, insertChars, positionAfterInsert: INT];
This routine actually does all the editing work for voice. It is passed a selection specification and a sound specification. If the selection is of non-zero length it is deleted; then if the sound is non-nil it is inserted. If the result of the call is that the contents of the viewer become NIL and destroyOK is TRUE, then the viewer is deleted.
selection.voiceViewerInfo will be updated to contain the new rope; selection.ropeInterval is only used for its start and length fields.
If leaveSelected THEN primary selection will be a point at the cut point (soundInterval = NIL) or select the inserted interval; otherwise the primary selection will disappear. The secondary selection always disappears.
Delete: PROC;
Deletes the primary selection
Copy: PROC [target: TEditDocument.SelectionId ← primary];
Copies the contents of the other selection to the target selection
Transpose: PROC [target: TEditDocument.SelectionId ← primary];
Transposes the contents of the primary and secondary selections
DictationOps
The special ops provided to support the "stop, listen, [erase], resume" model of a dictation machine
DictationOps: Menus.MenuProc;
The button to toggle the dictation menu on and off the screen
ToggleDictationMenu: PROC [viewer: ViewerClasses.Viewer];
Manual version of same thing
VoiceAging
Operations that add colors to and age colors in voice viewers. The dictation ops buttons in voice viewers may use the ages of sections of a viewer to determine where to delete/playback/resume recording.
ReColorViewer: PROC [
viewer: ViewerClasses.Viewer, voiceViewerInfo: VoiceViewerInfo ← NIL,
repaint: BOOLEANFALSE];
A viewer has been edited, and the age list for it has been updated appropriately. Redo the color looks so that the age marks are once more in the correct places.
The repaint flag is set where we haven't just redrawn the complete viewer before the call.
AgeAllViewers: PROC [youngestViewer: ViewerClasses.Viewer];
An editing event has occured which ages all the voice viewers. The youngestViewer has had its contents redrawn prior to the call.
EditAges: PROC [
viewerInfo: VoiceViewerInfo,
unchangedHead, deleteChars, insertChars: INT];
This gets called when an edit is made to the contents of the viewer. It keeps the ageList in step with the edit.
SourceMarkerTracking
Procedures that maintain consistency of the records of parent viewers & positions within them attached to voice viewers, as the parent viewers are edited.
RegisterViewer: PROC[viewer: ViewerClasses.Viewer];
Called every time a source marker is created within a text viewer.
TrackDeletes: PROC;
Called before the primary selection is deleted, if in a text viewer.
VoiceMarkers
Handle markers in voice, of two types
i) in-capillary marker characters
ii) annotation of voice with text
AddCharMark: Menus.MenuProc;
DeleteCharMarks: Menus.MenuProc;
LockedAddCharMark: PROC [viewer: ViewerClasses.Viewer, position: INT];
DisplayCharMarks: PROC [
unMarkedRope: Rope.ROPE, charMarkList: LIST OF INT, skipChars: INT]
RETURNS [Rope.ROPE];
Called by SoundChars, which has been called to build the viewer corresponding to a soundlist, the first skipChars omitted. Takes that rope and replaces normal characters with marker characters as appropriate.
ExtractCharMarks: PUBLIC PROC [
viewerInfo: VoiceViewerInfo, soundInterval: SoundInterval];
Return as selection.charMarkList a copy (n.b.) of the section of the character mark list in viewerInfo falling in to the region of selection.ropeInterval.
EditCharMarks: PROC [
viewerInfo: VoiceViewerInfo, unchangedHead, deleteChars, insertChars: INT, soundInterval: SoundInterval];
Called when an edit is made to the contents of the viewer. Keeps the charMarkList in step with the edit.
TextInput: PROC [viewer: ViewerClasses.Viewer, input: Rope.ROPE];
All printable characters typed in to a voice viewer cause this procedure to be called.
BackSpace: PROC [viewer: ViewerClasses.Viewer];
Similarly BackSpacing gets this one called.
BackWord: PROC [viewer: ViewerClasses.Viewer];
Simile.
RedrawTextMarkers: PROC [
viewer: ViewerClasses.Viewer, voiceCharNode: TiogaOpsDefs.Ref];
Called by voicePlayBack's redraw procedure (under voiceLock) to place all the textual markers back into a voice viewer.
ExtractTextMarks: PUBLIC PROC [
viewerInfo: VoiceViewerInfo, soundInterval: SoundInterval];
Return as selection.textMarkList a copy (n.b.) of the section of the textual mark list in viewerInfo falling in to the region of selection.ropeInterval.
EditTextMarks: PROC [
viewerInfo: VoiceViewerInfo, unchangedHead, deleteChars, insertChars: INT,
soundInterval: SoundInterval];
Called when an edit is made to the contents of the viewer. Keeps the textMarkList in step with the edit.
RopeFromTextList: PROC [LIST OF TextMarkEntry] RETURNS [Rope.ROPE];
Convert the list of text markers in a voice viewer into a rope suitable for saving in a textual document at the position of a talks bubble.
TextListFromRope: PROC [Rope.ROPE] RETURNS [LIST OF TextMarkEntry];
And go the other way.
VoiceInText
The routines that integrate voice into normal tioga documents.
DebugRope: PROC [Rope.ROPE];
ChangeMenu: PROC [viewer: ViewerClasses.Viewer, subMenu: Menus.MenuEntry];
Causes the given submenu to toggle in and out of view for the given viewer.
Code lifted from TEditDocumentsImpl in Tioga—should really be public. Also it is bound into the Tioga idea that there are three possible sub-menus for any given viewer - "places", "levels" and one other supplied by the user. Using this code for viewers other than tioga viewers (as DictationOps does) is rather squalid.
ApplyToCharsInPrimarySelection: PROC [ActionProc: PROC [TiogaOpsDefs.Location]];
A generally useful procedure: applies ActionProc to every character in the primary selection, locking the selection first.
ApplyToLockedChars: PROC [ActionProc: PROC [TiogaOpsDefs.Location]];
As above but used where the selection is already locked.
StoreVoiceAtSelection: PROC [voiceViewerInfo: VoiceViewerInfo]
RETURNS [succeeded: BOOLEAN];
Place the supplied voice at the selection and make a source marker there.
PlaySelection: PROC;
Find any voice within the current selection and play it back (if more than one talks bubble, from left to right).
CancelProc: Menus.MenuProc;
An appropriate procedure to put behind any STOP button for recording/playback of voice.
DeleteVoiceFromChar: PROC [position: TiogaOpsDefs.Location];
DeleteSourceMarkers: PROC [viewer: ViewerClasses.Viewer, voiceViewerNumber: INT, exceptionLoc: TiogaOpsDefs.Location];
The specified viewer contains one or more source markers for the given voiceViewerNumber: delete all except the one at exceptionLoc.
SaveRopeAtSourceMarkers: PROC [viewer: ViewerClasses.Viewer, voiceViewerNumber: INT, voiceRopeID: Rope.ROPE, textInVoice: Rope.ROPE] RETURNS [succeeded: BOOLEAN];
Try to save the contents of the voice viewer voiceViewerNumber (specified as the voiceRopeID) somewhere in the given viewer (at associated source markers). Will fail if no source markers for the voiceViewerNumber are found.
END.
Swinehart, April 8, 1987 10:34:42 am PDT
Merged from DictationOps, SoundList, SourceMarkerTracking, VoiceAging, VoiceEditOps, VoiceInText, VoiceMarkers, VoicePlayBack, VoiceRecord, VoiceViewers interfaces, by S. Ades. Stephen had a different concept of interface granularity than is the norm in Cedar.
changes to: DictationOps
Polle Zellweger (PTZ) May 28, 1987 4:53:24 pm PDT
New procedure to allow Marking the current playback spot directly.
changes to: GetCurrentPlayBackPos, DIRECTORY
Polle Zellweger (PTZ) June 8, 1987 6:57:49 pm PDT
new: SetTiogaViewerParams, SetVoiceLooks.
Polle Zellweger (PTZ) June 17, 1987 3:45:33 pm PDT
new: MakeVoiceEdited, deleted: RemoveParentPointersTo
Polle Zellweger (PTZ) June 20, 1987 10:28:31 pm PDT
Source markers: handle copies (multiplicity within a doc) and copy/move to another doc (breaks link).
changes to: RemoveParentViewer, DeleteSourceMarkers, SaveRopeAtSourceMarkers
Polle Zellweger (PTZ) June 22, 1987 7:19:47 pm PDT
Fix bug in copying pending-delete voice when source and dest are in the same voice viewer.
changes to: ReplaceSelectionWithSavedInterval
Polle Zellweger (PTZ) July 10, 1987 1:35:30 pm PDT
Make voiceViewerInfoList into a real list; make private to VoiceViewersImpl.
changes to: VoiceViewerInfoList, VoiceViewerInfoRec, FindAttachedVoiceViewers, RemoveParentPointersTo
Polle Zellweger (PTZ) July 20, 1987 4:50:36 pm PDT
Add Finch/Thrush error handling (disappearing conversations, etc) + voicerope monitoring.
changes to: GetCurrentPlayBackPos, SetPlayBackState
Polle Zellweger (PTZ) July 21, 1987 12:11:31 pm PDT
changes to: DIRECTORY