<> <> <> <> <<>> <> <<>> 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; <<-- no BeepTune until we store SysNoises in the new voice database>> 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] = { <> < play after all other record/playback requests are satisfied.>> < playing is optional; leave connection open if tune doesn't exist.>> < wait until things appear to be started properly, or have failed.>> 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] = { <> < NULL;>> <<};>> 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. <> <>