DictationOpsImpl.mesa
special ops provided to support the "stop, listen, [erase,] resume" model of a dictation machine, also to compress silence periods after such a dictation session
Ades, September 23, 1986 8:24:24 pm PDT
all of the dictation operations stand apart from the normal rules for playback/recording, which are that one must be stopped manually before the other can be requested. A playback or record request in this module implicitly cancels any already in progress.
the dictation machine works differently for color and monochrome viewers: the end point for playback/resume and delete to-/record from is the end of the viewer for monochrome and the end of the youngest sound for the color
DIRECTORY
Commander USING [CommandProc, Register],
CommandTool USING [ArgumentVector, Parse, Failed],
Convert USING [IntFromRope],
Jukebox USING [bytesPerChirp],
Menus USING [MenuProc, MenuEntry, InsertMenuEntry, GetLine, SetLine, ChangeNumberOfLines],
MessageWindow USING [Append, Blink],
MBQueue USING [CreateMenuEntry],
SoundList USING [LastSilenceInSoundList],
TiogaOps USING [GetSelection, FirstChild, ViewerDoc, SaveSelA, RestoreSelA],
TiogaOpsDefs USING [Location, Ref],
ViewerClasses USING [Viewer],
ViewerOps USING [FetchProp],
VoiceAging USING [oldest],
VoiceEditOps USING [ReplaceSelectionWithSavedInterval],
VoiceInText USING [ChangeMenu, thrushHandle, voiceButtonQueue],
VoicePlayBack USING [CancelPlayBack, PlaySlabSection],
VoiceRecord USING [StopRecording, RecordInPlaceOfSelection],
VoiceRope USING [Stop, VoiceRopeInterval],
VoiceViewers USING [voiceViewerMenu, VoiceViewerInfo, soundRopeCharLength, GetVoiceLock, SelectionRec, IntPair, Selection, voiceViewerInfoList],
DictationOps;
DictationOpsImpl: CEDAR PROGRAM IMPORTS Commander, CommandTool, Convert, Menus, MessageWindow, MBQueue, SoundList, TiogaOps, ViewerOps, VoiceAging, VoiceEditOps, VoiceInText, VoicePlayBack, VoiceRecord, VoiceRope, VoiceViewers EXPORTS DictationOps = BEGIN
DictationOps:
PUBLIC Menus.MenuProc = {
the button to toggle the dictation menu on and off the screen
VoiceInText.ChangeMenu[NARROW[parent], dictationMenu] };
ToggleDictationMenu:
PUBLIC
PROC [viewer: ViewerClasses.Viewer] = {
manual version of same thing
VoiceInText.ChangeMenu[viewer, dictationMenu] };
FindEnd:
PROC [viewerInfo: VoiceViewers.VoiceViewerInfo]
RETURNS [sample:
INT] = {
procedure to interpret 'end' for monochrome/color viewers
IF viewerInfo.ropeInterval.start # 0 THEN ERROR;
there should be a 'real rope' in this viewer!!
IF ~viewerInfo.color THEN RETURN [viewerInfo.ropeInterval.length]
ELSE
{ minAge:
INT ← VoiceAging.oldest+2;
endOfAge: INT ← LAST[INT];
FOR l:
LIST
OF VoiceViewers.IntPair ← viewerInfo.ageList, l.rest
WHILE l #
NIL
DO
IF l.first.age <= minAge THEN
{ endOfAge ←
IF l.rest=
NIL
THEN
LAST[
INT]
ELSE l.rest.first.position;
minAge ← l.first.age
}
ENDLOOP;
RETURN [IF endOfAge = LAST[INT] THEN viewerInfo.ropeInterval.length ELSE endOfAge*VoiceViewers.soundRopeCharLength]
}
};
StillInVoiceViewerList: PROC [viewerInfo: VoiceViewers.VoiceViewerInfo] RETURNS [BOOLEAN] = {
if the supplied viewerInfo does not represent an existing voice viewer (having been deleted due to being empty) display and error message and return FALSE
nextInfo: VoiceViewers.VoiceViewerInfo ← VoiceViewers.voiceViewerInfoList;
DO
IF nextInfo = NIL THEN
{
MessageWindow.Append["Voice viewer has been deleted (contents NIL): dictation operation therefore invalid", TRUE];
MessageWindow.Blink[];
RETURN[FALSE]
};
IF nextInfo = viewerInfo THEN RETURN[TRUE];
nextInfo ← nextInfo.nextInfoRec
ENDLOOP
PlayFromSelection: Menus.MenuProc = {
viewer: ViewerClasses.Viewer;
start: TiogaOpsDefs.Location;
viewerInfo: VoiceViewers.VoiceViewerInfo;
node: TiogaOpsDefs.Ref;
from, to: INT;
[viewer: viewer, start: start] ← TiogaOps.GetSelection[];
IF viewer = NIL
THEN
{ MessageWindow.Append["Make a selection first",
TRUE];
MessageWindow.Blink[];
RETURN
};
viewerInfo ← NARROW[ViewerOps.FetchProp[viewer, $voiceViewerInfo], VoiceViewers.VoiceViewerInfo];
IF viewerInfo = NIL
THEN
{ MessageWindow.Append["Make a selection in a voice viewer first",
TRUE];
MessageWindow.Blink[];
RETURN
};
VoiceRope.Stop[VoiceInText.thrushHandle];
VoicePlayBack.CancelPlayBack[];
VoiceRecord.StopRecording[];
IF ~StillInVoiceViewerList[viewerInfo] THEN RETURN;
**** need to take the voice lock before performing the following!
node ← TiogaOps.FirstChild[TiogaOps.ViewerDoc[viewer]];
from ← start.where;
to ← FindEnd[viewerInfo]/VoiceViewers.soundRopeCharLength;
IF to >= from
THEN VoicePlayBack.PlaySlabSection[viewer, node, from, to]
ELSE
{ MessageWindow.Append["Make a selection before the end of the most recent edit",
TRUE];
MessageWindow.Blink[]
};
ResumeFromSelection: Menus.MenuProc = {
viewer: ViewerClasses.Viewer;
start: TiogaOpsDefs.Location;
viewerInfo: VoiceViewers.VoiceViewerInfo;
node: TiogaOpsDefs.Ref;
replaceRopeInterval: VoiceRope.VoiceRopeInterval;
startDelete, endDelete: INT;
[viewer: viewer, start: start] ← TiogaOps.GetSelection[];
IF viewer = NIL
THEN
{ MessageWindow.Append["Make a selection first",
TRUE];
MessageWindow.Blink[];
RETURN
};
viewerInfo ← NARROW[ViewerOps.FetchProp[viewer, $voiceViewerInfo], VoiceViewers.VoiceViewerInfo];
IF viewerInfo = NIL
THEN
{ MessageWindow.Append["Make a selection in a voice viewer first",
TRUE];
MessageWindow.Blink[];
RETURN
};
VoiceRope.Stop[VoiceInText.thrushHandle];
VoicePlayBack.CancelPlayBack[];
VoiceRecord.StopRecording[];
IF ~StillInVoiceViewerList[viewerInfo] THEN RETURN;
IF ~VoiceViewers.GetVoiceLock[viewerInfo] THEN RETURN;
node ← TiogaOps.FirstChild[TiogaOps.ViewerDoc[viewer]];
startDelete ← start.where*VoiceViewers.soundRopeCharLength;
endDelete ← FindEnd[viewerInfo];
IF startDelete>endDelete THEN
{ MessageWindow.Append["Make a selection before the end of the most recent edit",
TRUE];
MessageWindow.Blink[];
viewerInfo.editInProgress ← FALSE;
RETURN
};
replaceRopeInterval ← [ropeID: viewerInfo.ropeInterval.ropeID, start: startDelete, length: endDelete - startDelete];
VoiceRecord.RecordInPlaceOfSelection[NEW[VoiceViewers.SelectionRec ← [viewer: viewer, voiceViewerInfo: viewerInfo, ropeInterval: replaceRopeInterval, displayNode: node]]]
};
ResumeFromEnd: Menus.MenuProc = {
viewer: ViewerClasses.Viewer ← NARROW[parent, ViewerClasses.Viewer];
viewerInfo: VoiceViewers.VoiceViewerInfo ← NARROW[ViewerOps.FetchProp[viewer, $voiceViewerInfo], VoiceViewers.VoiceViewerInfo];
node: TiogaOpsDefs.Ref;
replaceRopeInterval: VoiceRope.VoiceRopeInterval;
VoiceRope.Stop[VoiceInText.thrushHandle];
VoicePlayBack.CancelPlayBack[];
VoiceRecord.StopRecording[];
IF ~StillInVoiceViewerList[viewerInfo] THEN RETURN;
IF ~VoiceViewers.GetVoiceLock[viewerInfo] THEN RETURN;
node ← TiogaOps.FirstChild[TiogaOps.ViewerDoc[viewer]];
replaceRopeInterval ← [ropeID: viewerInfo.ropeInterval.ropeID, start: FindEnd[viewerInfo], length: 0];
VoiceRecord.RecordInPlaceOfSelection[NEW[VoiceViewers.SelectionRec ← [viewer: viewer, voiceViewerInfo: viewerInfo, ropeInterval: replaceRopeInterval, displayNode: node]]]
};
---------
routines which allow the lengths of silent portions within a rope to be adjusted
criticalSilenceLength: INT ← Jukebox.bytesPerChirp/2;
SetCriticalSilenceLength: Commander.CommandProc = {
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd ! CommandTool.Failed => {msg ← errorMsg; GOTO Quit}];
IF argv.argc # 2
THEN {
msg ← "Usage: SetCriticalSilenceLength lengthInMilliseconds";
GOTO Quit
};
criticalSilenceLength ← Convert.IntFromRope[argv[1]]*8;
RETURN
EXITS
Quit => RETURN [$Failure, msg]
};
AdjustSilences: Menus.MenuProc = {
viewer: ViewerClasses.Viewer;
viewerInfo: VoiceViewers.VoiceViewerInfo;
selectionToRemove: VoiceViewers.Selection;
startOfSilence, lengthOfSilence: INT;
viewer ← NARROW [parent, ViewerClasses.Viewer];
viewerInfo ← NARROW[ViewerOps.FetchProp[viewer, $voiceViewerInfo], VoiceViewers.VoiceViewerInfo];
IF ~VoiceViewers.GetVoiceLock[viewerInfo] THEN
{
MessageWindow.Append["Unable to examine silences in viewer - editing operation already in progress", TRUE];
MessageWindow.Blink[];
RETURN
};
TiogaOps.SaveSelA[];
DO
[startsAt: startOfSilence, lasts: lengthOfSilence] ← SoundList.LastSilenceInSoundList[soundList: viewerInfo.soundList, lengthGreaterThan: criticalSilenceLength];
IF lengthOfSilence = -1 THEN
{ viewerInfo.editInProgress ← FALSE;
TiogaOps.RestoreSelA[];
RETURN
};
selectionToRemove ← NEW[VoiceViewers.SelectionRec ← [viewer: viewer, voiceViewerInfo: viewerInfo, ropeInterval: [ropeID: viewerInfo.ropeInterval.ropeID, start: startOfSilence+criticalSilenceLength, length: lengthOfSilence-criticalSilenceLength], displayNode: TiogaOps.FirstChild[TiogaOps.ViewerDoc[viewer]]]];
viewerInfo.edited ← TRUE;
[] ← VoiceEditOps.ReplaceSelectionWithSavedInterval[selection: selectionToRemove, soundInterval: NIL, leaveSelected: FALSE]
not interested in any of the returned values from this routine - in particular the viewer will not have been deleted by this call since we have only deleted on portion of one silent interval
this action really ought not to age the voice viewers - this requires an extra parameter to ReplaceSelectionWithSavedInterval. See the implementation of that routine - the call to VoicePlayBack.RedrawViewer can take a don't age option, which we currently don't exploit
ENDLOOP
};
dictationMenu: Menus.MenuEntry;
Menus.InsertMenuEntry[VoiceViewers.voiceViewerMenu, MBQueue.CreateMenuEntry[VoiceInText.voiceButtonQueue, "AdjustSilences", AdjustSilences], 1];
Menus.InsertMenuEntry[VoiceViewers.voiceViewerMenu, MBQueue.CreateMenuEntry[VoiceInText.voiceButtonQueue, "ResumeFromEnd", ResumeFromEnd], 1];
Menus.InsertMenuEntry[VoiceViewers.voiceViewerMenu, MBQueue.CreateMenuEntry[VoiceInText.voiceButtonQueue, "ResumeFromSelection", ResumeFromSelection], 1];
Menus.InsertMenuEntry[VoiceViewers.voiceViewerMenu, MBQueue.CreateMenuEntry[VoiceInText.voiceButtonQueue, "PlayFromSelection", PlayFromSelection], 1];
dictationMenu ← Menus.GetLine[VoiceViewers.voiceViewerMenu, 1];
Menus.SetLine[VoiceViewers.voiceViewerMenu, 1, NIL];
Menus.ChangeNumberOfLines[VoiceViewers.voiceViewerMenu, 1];
Commander.Register["SetCriticalSilenceLength", SetCriticalSilenceLength, "SetCriticalSilenceLength lengthInMilliseconds - when the AdjustSilences button is bugged for any voice viewer, all silences of more than this length will be reduced to this length"];
END.