<> <> <> <<>> <> <<>> <> <<>> DIRECTORY BasicTime USING [earliestGMT, Now, Period], BluejayUtils USING [DescribeTune, DescribeInterval], BluejayUtilsRpcControl USING [ImportInterface, UnimportInterface], Convert USING [IntFromRope, RopeFromInt], FinchSmarts USING [CurrentFinchState, FinchState, GetProcs, Procs, RecordReason], FS USING [ComponentPositions, ExpandName], IO USING [card, GetCard, GetInt, int, PutFR, RIS, rope, STREAM], LoganBerryStub USING [AttributeType, AttributeValue, Entry, Error, ErrorCode, Open, OpenDB, ReadEntry, WriteEntry], LupineRuntime USING [BindingError], Rope USING [Cat, Concat, ROPE, Substr], <> Thrush USING [EncryptionKey, IntervalSpecs, Tune, VoiceInterval], UserProfile USING [Token], UserCredentials USING [CredentialsChangeProc, Get, RegisterForChange], VoiceUtils USING [Problem, RnameToRspec], VoiceRope; VoiceRopeImpl: CEDAR PROGRAM -- Should this be a monitor? IMPORTS BasicTime, BluejayUtils, BluejayUtilsRpcControl, Convert, FinchSmarts, FS, IO, LupineRuntime, Rope, UserCredentials, UserProfile, VoiceUtils, LoganBerry: LoganBerryStub EXPORTS VoiceRope ~ BEGIN OPEN VoiceRope; ROPE: TYPE ~ Rope.ROPE; STREAM: TYPE ~ IO.STREAM; defaultHandle: Handle_NIL; <> <> Open: PUBLIC PROC[voiceRopeDBName: Rope.ROPE _ NIL, voiceRopeDBInstance: Rope.ROPE _ NIL, localName: Rope.ROPE _ NIL, Complain: PROC[complaint: Rope.ROPE]_NIL] RETURNS [handle: Handle] = { <> server: Rope.ROPE _ UserProfile.Token["ThrushClientServerInstance", "Strowger.Lark"]; vdbHandle: VoiceDBHandle; ec: LoganBerry.ErrorCode; expl: ROPE; IF Complain = NIL THEN Complain _ FinchProblem; IF voiceRopeDBInstance = NIL THEN voiceRopeDBInstance _ server; IF voiceRopeDBName = NIL THEN voiceRopeDBName _ Rope.Cat["///", VoiceUtils.RnameToRspec[server].simpleName, "/VoiceRopeDB"]; [vdbHandle, ec, expl] _ VoiceRopeDB.Open[voiceRopeDBName]; IF ec#NIL THEN Complain[expl]; -- Trouble opening; success of future calls in doubt ImportBluejay[instance]; 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; }; }; Record: PUBLIC PROC[handle: Handle _ NIL] RETURNS [voiceRope: VoiceRope] = { <> ENABLE LoganBerry.Error => { handle.Complain[explanation]; CONTINUE; }; <> interval: Thrush.VoiceInterval; key: Thrush.EncryptionKey; reason: FinchSmarts.RecordReason; tune: Thrush.Tune; handle _ ValidateHandle[handle]; IF StartFinch[handle]#running THEN RETURN; <<-- no BeepTune until we store SysNoises in the new voice database>> <> [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; voiceRope _ NEW[VoiceRopeInterval]; [voiceRope.ropeID, voiceRope.length] _ VoiceRopeDB.Write[handle: handle.vdbHandle, struct: SimpleTuneList[tune, interval, key]]; }; <> 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 LoganBerry.Error => { handle.Complain[explanation]; CONTINUE; }; tune: Thrush.Tune; interval: Thrush.VoiceInterval; key: Thrush.EncryptionKey; struct: TuneList; handle _ ValidateHandle[handle]; IF StartFinch[handle]#running THEN RETURN; struct _ ReadVoiceRope[handle.vdbHandle, voiceRope].struct; IF struct=NIL THEN { handle.Complain["No such voice rope to play."]; RETURN;}; UNTIL struct=NIL DO [tune, interval, key, struct] _ 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, voiceRope: VoiceRope, refID: Rope.ROPE, refIDType: Rope.ROPE ] ~ { handle _ ValidateHandle[handle]; }; Forget: PUBLIC PROC [ <> <> handle: Handle _ NIL, refID: Rope.ROPE, refIDType: Rope.ROPE ] ~ { handle _ ValidateHandle[handle]; }; <> Cat: PUBLIC PROC [handle: Handle _ NIL, vr1, vr2, vr3, vr4, vr5: VoiceRope _ NIL] RETURNS [new: VoiceRope] ~ { <> struct: TuneList; IF vr1 = NIL THEN RETURN[NIL]; handle _ ValidateHandle[handle]; struct _ ReadVoiceRope[handle.vdbHandle, vr1].struct; IF vr2 # NIL THEN struct _ CatEntries[struct, ReadVoiceRope[handle.vdbHandle, vr2].struct]; IF vr3 # NIL THEN struct _ CatEntries[struct, ReadVoiceRope[handle.vdbHandle, vr3].struct]; IF vr4 # NIL THEN struct _ CatEntries[struct, ReadVoiceRope[handle.vdbHandle, vr4].struct]; IF vr5 # NIL THEN struct _ CatEntries[struct, ReadVoiceRope[handle.vdbHandle, vr5].struct]; new _ WriteVoiceRope[handle.vdbHandle, struct]; }; <<>> Substr: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope, start: INT _ 0, len: INT _ LAST[INT]] RETURNS [new: VoiceRope] ~ { <> struct: TuneList; handle _ ValidateHandle[handle]; struct _ ReadVoiceRope[handle.vdbHandle, vr].struct; IF struct=NIL THEN { handle.Complain["No such voice rope."]; RETURN[NIL];}; struct _ TuneListInterval[struct, start, len]; new _ WriteVoiceRope[handle.vdbHandle, struct]; }; <<>> Replace: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope, start: INT _ 0, len: INT _ LAST[INT], with: VoiceRope _ NIL] RETURNS [new: VoiceRope] ~ { <> base, struct: TuneList; handle _ ValidateHandle[handle]; base _ ReadVoiceRope[handle.vdbHandle, vr].struct; IF base=NIL THEN { handle.Complain["No such voice rope."]; RETURN[NIL];}; struct _ TuneListInterval[CopyEntry[base], 0, start]; struct _ CatEntries[struct, ReadVoiceRope[handle.vdbHandle, with].struct]; struct _ CatEntries[struct, TuneListInterval[base, start+len]]; new _ WriteVoiceRope[handle.vdbHandle, struct]; }; <<>> Length: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope] RETURNS [len: INT] ~ { <> header: LoganBerry.Entry; value: LoganBerry.AttributeValue; handle _ ValidateHandle[handle]; header _ ReadVoiceRope[handle.vdbHandle, vr].header; IF header = NIL THEN { handle.Complain["No such voice rope."]; RETURN[0];}; value _ GetAttributeValue[entry: header, type: $Length]; len _ Convert.IntFromRope[value]; }; <<>> <> DescribeRope: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope, minSilence: INT _ -1] RETURNS [noise: IntervalSpecs] ~ { struct: 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.vdbHandle, vr].struct; UNTIL struct=NIL DO [tune: tune, interval: interval, rest: struct] _ 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]]; }; <> CatEntries: PROC [e1, e2: LoganBerry.Entry] RETURNS [LoganBerry.Entry] ~ { <> ptr: LoganBerry.Entry _ e1; IF ptr = NIL THEN RETURN[e2]; UNTIL ptr.rest = NIL DO ptr _ ptr.rest; ENDLOOP; ptr.rest _ e2; RETURN[e1]; }; CopyEntry: PROC [entry: LoganBerry.Entry] RETURNS [LoganBerry.Entry] ~ { <> new, end: LoganBerry.Entry; IF entry = NIL THEN RETURN[NIL]; new _ LIST[entry.first]; end _ new; FOR e: LoganBerry.Entry _ entry.rest, e.rest WHILE e # NIL DO end.rest _ LIST[e.first]; end _ end.rest; ENDLOOP; RETURN[new]; }; GetAttributeValue: PROC [entry: LoganBerry.Entry, type: LoganBerry.AttributeType] RETURNS [LoganBerry.AttributeValue] ~ { FOR e: LoganBerry.Entry _ entry, e.rest WHILE e # NIL DO IF e.first.type = type THEN RETURN[e.first.value]; ENDLOOP; RETURN[NIL]; }; <<>> END. <> <>