DIRECTORY BluejayUtils USING [DescribeTune, DescribeInterval], BluejayUtilsRpcControl USING [ImportInterface, UnimportInterface], FinchSmarts USING [CurrentFinchState, FinchState, GetProcs, Procs, RecordReason], LupineRuntime USING [BindingError], Rope USING [Cat, ROPE], Thrush USING [EncryptionKey, IntervalSpecs, Tune, VoiceInterval], UserProfile USING [Token], VoiceUtils USING [Problem, RnameToRspec], VoiceRopeDB, VoiceRope; VoiceRopeImpl: CEDAR PROGRAM -- Should this be a monitor? IMPORTS BluejayUtils, BluejayUtilsRpcControl, FinchSmarts, LupineRuntime, Rope, UserProfile, VoiceUtils, VoiceRopeDB EXPORTS VoiceRope ~ BEGIN OPEN VoiceRope; ROPE: TYPE ~ Rope.ROPE; defaultHandle: Handle_NIL; Open: PUBLIC PROC[voiceRopeDBName: Rope.ROPE _ NIL, localName: Rope.ROPE _ NIL, Complain: PROC[complaint: Rope.ROPE]_NIL] RETURNS [handle: Handle] = { ENABLE VoiceRopeDB.Error => { -- Trouble opening; success of future calls in doubt handle.Complain[explanation]; CONTINUE; }; server: Rope.ROPE _ UserProfile.Token["ThrushClientServerInstance", "Strowger.Lark"]; vdbHandle: VoiceRopeDB.Handle; IF Complain = NIL THEN Complain _ FinchProblem; IF voiceRopeDBName = NIL THEN voiceRopeDBName _ Rope.Cat["/",server,"//",VoiceUtils.RnameToRspec[server].simpleName,"/VoiceRopeDB.df"]; vdbHandle _ VoiceRopeDB.Open[voiceRopeDBName]; ImportBluejay[server]; handle _ NEW[HandleRec _ [vdbHandle: vdbHandle, Complain: Complain]]; }; FinchProblem: PROC[complaint: Rope.ROPE] = { VoiceUtils.Problem[complaint, $Finch]; }; StartFinch: PROC[handle: Handle, complain: BOOL_TRUE] RETURNS [state: FinchSmarts.FinchState] = { RW: PROC[c: Rope.ROPE] = { IF ~complain THEN RETURN; handle.Complain[complaint: c]; }; SELECT (state _ FinchSmarts.CurrentFinchState[]) FROM unknown => RW["Sorry, Finch needs to be loaded and started.\n"]; stopped => RW["Sorry, Finch needs to be connected to telephone server.\nUse \"Finch\" command.\n"]; running => NULL; ENDCASE => ERROR; handle.procs _ IF state=unknown THEN NIL ELSE FinchSmarts.GetProcs[]; }; ValidateHandle: PROC[oldHandle: Handle] RETURNS [handle: Handle] = { handle _ oldHandle; IF handle=NIL THEN { IF defaultHandle=NIL THEN defaultHandle _ Open[]; handle _ defaultHandle; }; }; beep: VoiceRope _ NIL; -- beep to prompt recording Record: PUBLIC PROC[handle: Handle _ NIL] RETURNS [voiceRope: VoiceRope] = { ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; }; interval: Thrush.VoiceInterval; key: Thrush.EncryptionKey; reason: FinchSmarts.RecordReason; tune: Thrush.Tune; info: VoiceRopeDB.VoiceRopeInfo; handle _ ValidateHandle[handle]; IF StartFinch[handle]#running THEN RETURN; IF beep = NIL THEN beep _ GetByInterest[handle: handle, class: "SysNoises", refID: "BeepTune"]; IF beep # NIL THEN Play[handle: handle, voiceRope: beep, failOK: TRUE] ELSE handle.Complain["Can't play BEEP..."]; [reason, tune, interval, key] _ handle.procs.recordTune[queueIt: TRUE]; IF reason#ok THEN RETURN; IF interval.length = -1 THEN interval.length _ BluejayUtils.DescribeTune[tune].size * 8000; info _ VoiceRopeDB.WriteVoiceRope[handle: handle.vdbHandle, struct: VoiceRopeDB.SimpleTuneList[tune, interval, key]]; voiceRope _ NEW[VoiceRopeInterval _ [ropeID: info.vrID, start: 0, length: info.length]]; }; Play: PUBLIC PROC[handle: Handle_NIL, voiceRope: VoiceRope, queueIt: BOOL_TRUE, failOK: BOOL_FALSE, wait: BOOL_FALSE] = { ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; }; tune: Thrush.Tune; interval: Thrush.VoiceInterval; key: Thrush.EncryptionKey; struct: VoiceRopeDB.TuneList; handle _ ValidateHandle[handle]; IF StartFinch[handle]#running THEN RETURN; struct _ ReadVoiceRope[handle, voiceRope]; IF struct=NIL THEN { handle.Complain["No such voice rope to play."]; RETURN;}; UNTIL struct=NIL DO [tune, interval, key, struct] _ VoiceRopeDB.NextTuneOnList[struct]; []_handle.procs.playbackTune[tune: tune, interval: interval, key: key, queueIt: queueIt, failOK: failOK, wait: wait]; ENDLOOP; }; Stop: PUBLIC PROC[handle: Handle _ NIL] = { ENABLE UNWIND => NULL; handle _ ValidateHandle[handle]; IF StartFinch[handle, FALSE]#running THEN RETURN; handle.procs.stopTune[]; }; Retain: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope, class: InterestClass, refID: ROPE, other: ROPE _ NIL] ~ { ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; }; info: VoiceRopeDB.InterestInfo; info.vrID _ vr.ropeID; info.class _ class; info. refID _ refID; info.data _ other; handle _ ValidateHandle[handle]; VoiceRopeDB.AddInterest[handle.vdbHandle, info]; }; Forget: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope, class: InterestClass, refID: ROPE] ~ { ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; }; info: VoiceRopeDB.InterestInfo; info.vrID _ vr.ropeID; info.class _ class; info. refID _ refID; handle _ ValidateHandle[handle]; VoiceRopeDB.DropInterest[handle.vdbHandle, info]; }; GetByInterest: PUBLIC PROC [handle: Handle _ NIL, class: InterestClass, refID: ROPE] RETURNS [voiceRope: VoiceRope] ~ { ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; }; interest: VoiceRopeDB.Interest; info: VoiceRopeDB.InterestInfo; handle _ ValidateHandle[handle]; interest _ VoiceRopeDB.InterestForRef[handle.vdbHandle, class, refID]; IF interest = NIL THEN RETURN[NIL]; info _ VoiceRopeDB.UnpackInterest[interest]; voiceRope _ NEW[VoiceRopeInterval _ [ropeID: info.vrID, start: 0, length: LAST[INT]]]; }; Cat: PUBLIC PROC [handle: Handle _ NIL, vr1, vr2, vr3, vr4, vr5: VoiceRope _ NIL] RETURNS [new: VoiceRope] ~ { ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; }; struct: VoiceRopeDB.TuneList; info: VoiceRopeDB.VoiceRopeInfo; IF vr1 = NIL THEN RETURN[NIL]; handle _ ValidateHandle[handle]; struct _ ReadVoiceRope[handle, vr1]; IF vr2 # NIL THEN struct _ CatEntries[struct, ReadVoiceRope[handle, vr2]]; IF vr3 # NIL THEN struct _ CatEntries[struct, ReadVoiceRope[handle, vr3]]; IF vr4 # NIL THEN struct _ CatEntries[struct, ReadVoiceRope[handle, vr4]]; IF vr5 # NIL THEN struct _ CatEntries[struct, ReadVoiceRope[handle, vr5]]; info _ VoiceRopeDB.WriteVoiceRope[handle.vdbHandle, struct]; new _ NEW[VoiceRopeInterval _ [ropeID: info.vrID, start: 0, length: info.length]]; }; Substr: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope, start: INT _ 0, len: INT _ LAST[INT]] RETURNS [new: VoiceRope] ~ { ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; }; struct: VoiceRopeDB.TuneList; info: VoiceRopeDB.VoiceRopeInfo; handle _ ValidateHandle[handle]; struct _ ReadVoiceRope[handle, vr]; IF struct=NIL THEN { handle.Complain["No such voice rope."]; RETURN[NIL];}; struct _ VoiceRopeDB.TuneListInterval[struct, [start, len]]; info _ VoiceRopeDB.WriteVoiceRope[handle.vdbHandle, struct]; new _ NEW[VoiceRopeInterval _ [ropeID: info.vrID, start: 0, length: info.length]]; }; Replace: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope, start: INT _ 0, len: INT _ LAST[INT], with: VoiceRope _ NIL] RETURNS [new: VoiceRope] ~ { ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; }; base, struct: VoiceRopeDB.TuneList; info: VoiceRopeDB.VoiceRopeInfo; handle _ ValidateHandle[handle]; base _ ReadVoiceRope[handle, vr]; IF base=NIL THEN { handle.Complain["No such voice rope."]; RETURN[NIL];}; struct _ VoiceRopeDB.TuneListInterval[CopyEntry[base], [0, start]]; struct _ CatEntries[struct, ReadVoiceRope[handle, with]]; struct _ CatEntries[struct, VoiceRopeDB.TuneListInterval[base, [start+len, LAST[INT]]]]; info _ VoiceRopeDB.WriteVoiceRope[handle.vdbHandle, struct]; new _ NEW[VoiceRopeInterval _ [ropeID: info.vrID, start: 0, length: info.length]]; }; Length: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope] RETURNS [len: INT] ~ { ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; }; header: VoiceRopeDB.Header; info: VoiceRopeDB.VoiceRopeInfo; handle _ ValidateHandle[handle]; header _ VoiceRopeDB.ReadVoiceRope[handle.vdbHandle, vr.ropeID].header; info _ VoiceRopeDB.UnpackHeader[header]; RETURN[info.length]; }; DescribeRope: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope, minSilence: INT _ -1] RETURNS [noise: IntervalSpecs] ~ { ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; }; struct: VoiceRopeDB.TuneList; tuneSpec: Thrush.IntervalSpecs; tune: Thrush.Tune; interval: Thrush.VoiceInterval; noiseEnd: IntervalSpecs _ NIL; -- last item on current list of noise intervals samples: INT _ 0; handle _ ValidateHandle[handle]; struct _ ReadVoiceRope[handle, vr]; UNTIL struct=NIL DO [tune: tune, interval: interval, rest: struct] _ VoiceRopeDB.NextTuneOnList[struct]; tuneSpec _ BluejayUtils.DescribeInterval[targetTune: tune, targetInterval: interval, minSilence: minSilence].intervals; UNTIL tuneSpec = NIL DO IF noiseEnd = NIL THEN { noise _ LIST[[start: samples + tuneSpec.first.interval.start - interval.start, length: tuneSpec.first.interval.length]]; noiseEnd _ noise; } ELSE { noiseEnd.rest _ LIST[[start: samples + tuneSpec.first.interval.start - interval.start, length: tuneSpec.first.interval.length]]; noiseEnd _ noiseEnd.rest; }; tuneSpec _ tuneSpec.rest; ENDLOOP; samples _ samples + interval.length; ENDLOOP; }; ImportBluejay: PROC[instance: ROPE] = { TRUSTED { BluejayUtilsRpcControl.UnimportInterface[!LupineRuntime.BindingError => CONTINUE]; }; BluejayUtilsRpcControl.ImportInterface[["BluejayUtils.Lark", instance]]; }; ReadVoiceRope: PROC [handle: Handle, vr: VoiceRope] RETURNS [struct: VoiceRopeDB.TuneList] ~ { IF vr = NIL THEN RETURN[NIL]; struct _ VoiceRopeDB.ReadVoiceRope[handle.vdbHandle, vr.ropeID].struct; struct _ VoiceRopeDB.TuneListInterval[struct, [vr.start, vr.length]]; }; CatEntries: PROC [e1, e2: VoiceRopeDB.TuneList] RETURNS [VoiceRopeDB.TuneList] ~ { ptr: VoiceRopeDB.TuneList _ e1; IF ptr = NIL THEN RETURN[e2]; UNTIL ptr.rest = NIL DO ptr _ ptr.rest; ENDLOOP; ptr.rest _ e2; RETURN[e1]; }; CopyEntry: PROC [entry: VoiceRopeDB.TuneList] RETURNS [VoiceRopeDB.TuneList] ~ { new, end: VoiceRopeDB.TuneList; IF entry = NIL THEN RETURN[NIL]; new _ LIST[entry.first]; end _ new; FOR e: VoiceRopeDB.TuneList _ entry.rest, e.rest WHILE e # NIL DO end.rest _ LIST[e.first]; end _ end.rest; ENDLOOP; RETURN[new]; }; END. ”VoiceRopeImpl.mesa Copyright Σ 1986, 1987 by Xerox Corporation. All rights reserved. Doug Terry, July 7, 1986 11:36:52 am PDT Swinehart, March 23, 1987 2:00:35 pm PST Operations for manipulating recorded voice. Intervoice operations (record and play) The following routines were adapted from RecordPlayImpl.mesa If voiceRopeDBName is omitted, a default based on the user profile choice of Thrush Server is invented. If Complain is omitted, VoiceUtils.Problem[...$Finch] is used. The localName parameter is currently ignored. Someday it might indicate a backup database in case the main database is inaccessible. default parameters set up handle Should arrange to load Finch if not loaded, start it if not started. At present, does neither, tells caller what's what, and complains if asked to. Records a voice rope, registers it , and returns its ID. A NIL return value indicates that something went wrong. Nothing much can be done about it; just tell the user. Should eventually log for system administrator's benefit. -- no BeepTune until we store SysNoises in the new voice database May need to call Bluejay to get actual length; note the size returned is in chirps so must multiply by 8000 to get samples Play a specified voice rope. The boolean arguments are interpreted as follows: queueIt => play after all other record/playback requests are satisfied. failOK => playing is optional; leave connection open if tune doesn't exist. wait => wait until things appear to be started properly, or have failed. Sender and Walnut Message viewer STOP buttons Voice Interests The VoiceRope interface simply allows clients to register (and unregister) interests in voice ropes. Garbage collection is actually done through VoiceCleanup.mesa. Registers a new interest in the voice rope. The voice rope will be retained until either a corresponding Forget is done or the class' garbage collection process determines that the voice rope is no longer referenced, e.g. refID no longer exists. Taken together, the vr, class, and refID must be unique. Repeated calls of Retain with the same parameters (ignoring other) will only register a single interest. The specified refID of the specified class drops its interest in the voice rope. The voice rope is not necessarily deleted, however, since other interests in the same voice rope may exist. Returns any voice rope that is of interest to the given class and refID; returns NIL if no such voice rope exists. N.B.: System noises, like Beeps and intolerably cute rollback tunes are indexed in the interest database as refID's like "beep", "rollback", and so on, under the class "SysNoises". Editing operations Concatenates together the non-NIL voice ropes to produce a new voice rope. Creates a new voice rope that is a substring of an existing voice rope. Creates a new voice rope in which the given interval of the voice rope "vr" is replaced by the voice rope "with". Returns the actual length of the voice rope. This operation ignores the start and length values specified in the voice rope. Thus, vr.start _ 0; vr.length _ Length[handle, vr] will restore a voice rope to its full contents. Information about voice ropes Fetch: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope, index: INT] RETURNS [VoiceSample]; ENABLE { RPC.ImportFailed => NULL; }; Miscellaneous Concatenates the two lists together (destructive to the first). Copies one list to another. Doug Terry, March 18, 1986 10:16:33 am PST created. Κ ˆ˜codešœ™KšœB™BKšœ(™(K™(—K™K™+K™šΟk ˜ Kšœ œ"˜4Jšœœ&˜BJšœ œ@˜QJšœœ˜#Kšœœœ˜Jšœœ5˜AJšœ œ ˜Kšœ œ˜)Kšœ ˜ Kšœ ˜ —K˜KšΠbl œœœΟc˜:Jšœm˜tKšœ ˜šœ˜Kšœ ˜K˜Kšœœœ˜K˜Jšœœ˜—head™'Kšœ)Ÿ™˜>—Jšœu˜uKšœ œI˜XJšœ˜—J˜š œœœœ!œœ œœœœ˜yšœO™OJšœ Ÿ<™GJšœ ŸA™KJšœŸ?œ™H—Jšœ6œ˜HJ˜J˜J˜J˜J˜ Jšœœœ˜*Jšœ*˜*Jš œœœœ.œ˜Nšœœ˜KšœC˜CJšœu˜uKšœ˜—J˜J˜—š œœœœ˜+Jšœ!Οbœ™-Jšœœœ˜J˜ Jšœœ œœ˜1Jšœ˜Jšœ˜——™K™€K˜š œœœœ.œ œœ˜sKšœš™šJšœ6œ˜HJšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜ Jšœ0˜0Jšœ˜J˜—š  œœœœ.œ˜`Kšœ½™½Jšœ6œ˜HJšœ˜Jšœ˜Jšœ˜Jšœ˜J˜ Jšœ1˜1Jšœ˜—K˜š   œœœœœœ˜wJšœs™sJšœ΄™΄Jšœ6œ˜HJšœ˜Jšœ˜J˜ JšœF˜FJš œ œœœœ˜#Jšœ,˜,Kšœ œ;œœ˜VJšœ˜——™š  œœœœ'œœ˜nK™JJšœ6œ˜HKšœ˜Jšœ ˜ Kš œœœœœ˜J˜ Kšœ$˜$šœœ˜Kšœ8˜8—šœœ˜Kšœ8˜8—šœœ˜Kšœ8˜8—šœœ˜Kšœ8˜8—Jšœ<˜