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 = { VoiceInText.ChangeMenu[NARROW[parent], dictationMenu] }; ToggleDictationMenu: PUBLIC PROC [viewer: ViewerClasses.Viewer] = { VoiceInText.ChangeMenu[viewer, dictationMenu] }; FindEnd: PROC [viewerInfo: VoiceViewers.VoiceViewerInfo] RETURNS [sample: INT] = { IF viewerInfo.ropeInterval.start # 0 THEN ERROR; 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] = { 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; 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]]] }; 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] 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. ¤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 the button to toggle the dictation menu on and off the screen manual version of same thing procedure to interpret 'end' for monochrome/color viewers there should be a 'real rope' in this viewer!! 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 **** need to take the voice lock before performing the following! --------- routines which allow the lengths of silent portions within a rope to be adjusted 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 --------- ʹ– "cedar" style˜™code™¡K™'K™—K™K™Þ—J˜šÏk ˜ Jšœ(˜(Jšœ2˜2J˜Jšœ˜JšœœO˜ZJšœœ˜$Jšœœ˜ J˜)Jšœ œ>˜LJšœ œ˜#Jšœœ ˜Jšœ œ ˜Jšœ˜Jšœ7˜7Jšœ œ.˜?Jšœœ#˜6Jšœ œ+˜