<> <> <<>> DIRECTORY Commander USING [ CommandProc, Register ], FinchSmarts USING [CurrentFinchState, FinchState, GetProcs, Procs, RecordReason], GVBasics USING [RName], IO, Log USING [Report], Menus USING [MenuProc], NamesGV USING [ AttributeSeq, AttributeSeqRec, GVSetAttributeSeq ], Nuthatch USING [AddInterest, CatalogVoiceFile, currentNUH, EncryptionKey, GetDirectoryEntry, GetFileID, ID, InitializeNuthatch, NuthatchUserHandle, NuthatchUserRec, Tune, VoiceFileID, VoiceInterval], NuthatchDB USING [CloseTransaction], Process USING [ Detach ], RecordingLock USING [GetLock, ReleaseLock], Rope USING [Equal, ROPE], BasicTime USING [Now, GMT], UserProfile USING [ Token ], ViewerClasses USING [Viewer], Walnuthatch, WalnutSendInternal USING [SenderReport], WalnutSendOps USING [AddToSendMenu, AppendHeaderLine, userRName], WalnutWindow USING [ AddToMsgMenu, GetMsgName ] ; WalnuthatchImpl: CEDAR MONITOR IMPORTS BasicTime, Commander, FinchSmarts, IO, Log, NamesGV, Nuthatch, NuthatchDB, Process, RecordingLock, Rope, UserProfile, Walnuthatch, WalnutSendInternal, WalnutSendOps, WalnutWindow EXPORTS Walnuthatch = BEGIN OPEN IO, Walnuthatch; procs: FinchSmarts.Procs_NIL; RecordProc: ENTRY Menus.MenuProc = { ENABLE UNWIND => NULL; viewer: ViewerClasses.Viewer_ NARROW[parent]; nuthatchUserHandle: Nuthatch.NuthatchUserHandle; <> IF StartFinch[]#running THEN RETURN; IF RecordingLock.GetLock[] THEN TRUSTED { -- Spawn process that can do the recording so this one can call StopRecording if it wants to. nuthatchUserHandle_ Nuthatch.currentNUH; Process.Detach[FORK DoRecordProc[nuthatchUserHandle, viewer] ]; } ELSE WalnutSendInternal.SenderReport[msg: "Recorder busy now.\n"] }; DoRecordProc: PROC [ nuthatchUserHandle: Nuthatch.NuthatchUserHandle, viewer: ViewerClasses.Viewer] = { ENABLE UNWIND => NULL; startSample: INT; samples: INT; key: Nuthatch.EncryptionKey; reason: FinchSmarts.RecordReason; time: BasicTime.GMT; tune: Nuthatch.Tune; voiceFileID: Nuthatch.VoiceFileID; <> [] _ procs.playNoise[noiseName: $beep, failOK: TRUE]; <> [reason, tune, [startSample, samples], key] _ procs.recordTune[queueIt: TRUE]; RecordingLock.ReleaseLock[]; Nuthatch.currentNUH.tune_ tune; Nuthatch.currentNUH.startSample_ startSample; Nuthatch.currentNUH.samples_ samples; Nuthatch.currentNUH.key_ key; IF reason#ok THEN { WalnutSendInternal.SenderReport[msg: "Attempt to record failed\n"]; RETURN; }; time_BasicTime.Now[]; voiceFileID_IO.PutFR["%d %t", IO.int[tune], IO.time[time]]; WalnutSendOps.AppendHeaderLine[viewer, IO.PutFR["VoiceFileID: %g", IO.rope[voiceFileID]]]; Nuthatch.CatalogVoiceFile[ nuthatchUserHandle: nuthatchUserHandle, voiceFileID: voiceFileID, tuneNumber: tune, recordedTime: time, samples: samples, startingSample: startSample, encryptionKey: key, time: time, type: nuthatchUserHandle.defaultType ]; }; StopProc, StopPlayProc: ENTRY Menus.MenuProc = { ENABLE UNWIND => NULL; IF StartFinch[FALSE]#running THEN RETURN; procs.stopTune[]; }; AttachProc: ENTRY Menus.MenuProc = { <> ENABLE UNWIND => NULL; viewer: ViewerClasses.Viewer_ NARROW[parent]; WalnutSendOps.AppendHeaderLine[viewer, "VoiceFileID: \001VoiceFileID\002"]; }; PlaybackProc: ENTRY Menus.MenuProc = { -- for playing message most recently recorded -- ENABLE UNWIND => NULL; interval: Nuthatch.VoiceInterval; key: Nuthatch.EncryptionKey; samples: INT; startSample: INT; tune: Nuthatch.Tune; viewer: ViewerClasses.Viewer_ NARROW[parent]; <> IF StartFinch[]#running THEN RETURN; tune_Nuthatch.currentNUH.tune; samples_Nuthatch.currentNUH.samples; startSample_Nuthatch.currentNUH.startSample; key_Nuthatch.currentNUH.key; interval _ []; []_procs.playbackTune[tune: tune, interval: interval, key: key]; }; PlayProc: ENTRY Menus.MenuProc = { -- for playing an old recorded message -- ENABLE UNWIND => NULL; found: BOOL; GVID: Rope.ROPE; key: Nuthatch.EncryptionKey; nuthatchUserHandle: Nuthatch.NuthatchUserHandle; tune: Nuthatch.Tune; viewer: ViewerClasses.Viewer_ NARROW[parent]; voiceFileID: Nuthatch.VoiceFileID; IF StartFinch[]#running THEN RETURN; GVID_ WalnutWindow.GetMsgName[viewer]; nuthatchUserHandle_ Nuthatch.currentNUH; voiceFileID _ Nuthatch.GetFileID[GVID, nuthatchUserHandle, FALSE]; [tune, , , , , , key, , , found] _ Nuthatch.GetDirectoryEntry[voiceFileID]; IF found THEN []_procs.playbackTune[tune: tune, interval: [], key: key]; }; MoveTo: PUBLIC ENTRY PROC [ msgset: Rope.ROPE, nuthatchUserHandle: Nuthatch.NuthatchUserHandle, RefID: Nuthatch.ID] = { <> ENABLE UNWIND => NULL; time: BasicTime.GMT; IF (Rope.Equal[msgset, "deleted",FALSE] OR Rope.Equal[msgset, "active", FALSE]) THEN RETURN; time_BasicTime.Now[]; Nuthatch.AddInterest[nuthatchUserHandle: nuthatchUserHandle, refID: RefID, time: time]; }; StartFinch: INTERNAL PROC[complain: BOOL_TRUE] RETURNS [state: FinchSmarts.FinchState] = { <> <> RW: PROC[c: Rope.ROPE] = { IF ~complain THEN RETURN; WalnutSendInternal.SenderReport[msg: 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 or push \"Participate\" button in the Finch viewer.\n"]; running => NULL; ENDCASE => ERROR; procs _ IF state=unknown THEN NIL ELSE FinchSmarts.GetProcs[]; }; <> GetSysNoises: ENTRY PROC[nuthatchUserHandle: Nuthatch.NuthatchUserHandle] = TRUSTED { ENABLE { UNWIND => NULL; ANY=>CONTINUE; }; resultName: Rope.ROPE _ UserProfile.Token[key: "ThrushClientServerInstance", default: "Strowger.Lark"]; GetOne: INTERNAL PROC[fileID: Rope.ROPE, attrID: ATOM] = TRUSTED { key: Nuthatch.EncryptionKey; tune: Nuthatch.Tune; tuneFound: BOOL; cardKey: LONG POINTER TO ARRAY[0..2) OF LONG CARDINAL=LOOPHOLE[LONG[@key]]; voiceFileID: Nuthatch.VoiceFileID = Nuthatch.GetFileID[fileID, nuh, FALSE]; IF voiceFileID = NIL THEN RETURN; [tune, , , , , , key, , , tuneFound] _ Nuthatch.GetDirectoryEntry[voiceFileID, FALSE]; IF tuneFound THEN { entry: NamesGV.AttributeSeq _ NEW[NamesGV.AttributeSeqRec[2]]; entry[0] _ [$unspec, IO.PutR[int[tune]]]; entry[1] _ [$unspec, IO.PutFR["%bB %bB", card[cardKey[0]], card[cardKey[1]]]]; entry.length _ 2; NamesGV.GVSetAttributeSeq[resultName, attrID, entry]; }; }; nuh: Nuthatch.NuthatchUserHandle; IF nuthatchUserHandle=NIL THEN RETURN; nuh _ NEW[Nuthatch.NuthatchUserRec _ nuthatchUserHandle^]; nuh.userName _ UserProfile.Token[key: "ThrushServerInstance", default: "Morley.Lark"]; nuh.refIDType _ "SysNoises"; GetOne["Rollback", $rollback]; GetOne["BeepTune", $beep]; GetOne["CedarBoot", $boot]; GetOne["Checkpoint", $checkpoint]; GetOne["WholeRollback", $rollbacksong]; GetOne["WorldSwap", $worldswap]; NuthatchDB.CloseTransaction[]; }; <> InitializeWalnuthatch: Commander.CommandProc = { userName: GVBasics.RName = WalnutSendOps.userRName; success: BOOL; nuthatchUserHandle: Nuthatch.NuthatchUserHandle; [success, nuthatchUserHandle] _ Nuthatch.InitializeNuthatch[userName: userName, RefIDType: "GVID", close: TRUE]; IF NOT success THEN { Log.Report["Problem with nuthatch database or log!", $Finch]; RETURN; }; IF nuthatchUserHandle#Nuthatch.currentNUH THEN ERROR; -- paranoia -- Sets up the button procs needed by Walnut. WalnutSendOps.AddToSendMenu[label: "Record", proc: RecordProc]; WalnutSendOps.AddToSendMenu[label: "STOP!", proc: StopProc]; WalnutSendOps.AddToSendMenu[label: "Play", proc: PlaybackProc]; WalnutSendOps.AddToSendMenu[label: "Attach", proc: AttachProc]; WalnutWindow.AddToMsgMenu[label: "Play", proc: PlayProc]; WalnutWindow.AddToMsgMenu[label: "STOP!", proc: StopPlayProc]; Walnuthatch.InitializeWalnutVoice[]; }; GetSysNoisesCmd: Commander.CommandProc = { <> GetSysNoises[Nuthatch.currentNUH]; }; Commander.Register["GetSysNoises", GetSysNoisesCmd, "Transfer Rollback, BeepTune, CedarBoot, Checkpoint, WholeRollback, WorldSwap noises to GV; Execute this after any changes to any of these tunes."]; Commander.Register["WalnutVoice", InitializeWalnuthatch, "Register WalnutVoice with walnut, extend WalnutSend menu."]; END.