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. zWalnuthatchImpl.mesa Last Edited by: Swinehart, October 17, 1985 12:20:30 pm PDT Try to grab the right to record. If possible, go on and do it. If not, report failure to the caller. Call Finch Recorder, get back tune data. Compose VoiceFileID from tune number and time. reported intervals will be ignored by smarts? Add a line to the Sender in which a VoiceFileID can be inserted. -- Get tune, interval, and key of the most recently recorded voice from handle. -- When a Walnut message is moved to another message set, MoveTo calls Nuthatch to report a change in the attached voice message's reference count. Called just for voice messages or for all of them? 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. Rollback hack. End of Rollback hack. HACK HACK HACK HACK HACK HACK! ÊŠ˜J™J™;J™šÏk ˜ J˜*Jšœ œ@˜QJšœ œ ˜Jšœ˜Jšœœ ˜Jšœœ ˜J˜CJšœ œZœ]˜ÇJšœ œ˜$Jšœœ ˜Jšœœ˜+Jšœœ œ˜Jšœ œœ˜Jšœ œ ˜Jšœœ ˜J˜ Jšœœ˜(Jšœœ.˜AJšœ/˜/Jšœ˜J˜—šœœœ˜š˜Jšœ#œ˜²—Jšœ˜Jšœœœ˜J˜—Jšœœ˜J˜šÏn œœ˜%Jšœœœ˜Jšœœ ˜.J˜1Jšœf™fJšœœœ˜$Jšœœœ˜)šÏc]˜`Jšœ(˜(Jšœœ,˜?J˜—Jšœ=˜AJšœ˜—J˜šž œœ˜Jšœ0˜0Jšœ!˜!Jšœœœ˜Jšœ œ˜Jšœ œ˜ J˜J˜!J˜J˜J˜"J˜JšœX™X—˜šœ/œ˜5Jšœ-™-—JšœIœ˜OJ˜J˜J˜-J˜%J˜šœ ˜JšœN˜N—J˜J˜Jšœ œœ œ ˜;Jšœ'œœ˜[˜J˜'J˜J˜J˜J˜J˜J˜J˜ J˜$J˜—Jšœ˜—J˜šžœž œœ˜0Jšœœœ˜Jšœ œ œœ˜)Jšœ˜Jšœ˜J˜—šž œœ˜$JšœC™CJšœœœ˜Jšœœ ˜-J˜Lšœ˜J˜——šž œœŸ0˜WJšœœœ˜J˜!J˜Jšœ œ˜ Jšœ œ˜J˜Jšœœ ˜-JšœO™OJšœœœ˜$J˜J˜$J˜,J˜J˜J˜@Jšœ˜J˜—šžœœŸ)˜LJšœœœ˜Jšœœ˜ Jšœœ˜J˜J˜0J˜Jšœœ ˜-J˜"J˜Jšœœœ˜$Jšœ"˜&Jšœ(˜(Jšœ!œœ˜BJ˜KJšœœ;˜HJšœ˜—J˜šžœœœœ˜Jšœ œCœ˜[JšœÇ™ÇJšœœœ˜J˜Jš œœœœ œ˜]J˜J˜WJ˜—J˜š ž œœœ œœ˜.Jšœ$˜+J™DJ™Nšœœ œ˜Jšœ œœ˜Jšœ(˜(J˜—šœ+˜5Jšœ œ3˜@Jšœ œ‰˜–Jšœ œ˜Jšœœ˜—Jš œœœœœ˜>J˜—J˜J˜J™šž œœœ4œ˜UJš œœœœœ˜*šœg˜gJ˜—š žœœœœ œœ˜BJ˜J˜Jšœ œ˜Jšœ œœœœœœœœœ˜KJšœDœ˜KJšœœœœ˜!JšœOœ˜Všœ œ˜Jšœœ˜>J˜)Jšœœ7˜NJ˜Jšœ5˜5J˜—J˜J˜—Jšœ!˜!Jšœœœœ˜&Jšœœ1˜:JšœV˜VJ˜J˜J˜J˜J˜"J˜'J˜ J˜J˜—J™J˜šžœœ˜1J˜3Jšœ œ˜Jšœ0˜0šœ˜JšœJœ˜P—šœœ œ˜Jšœ>œ˜H—Jšœ(œœŸ ˜AJšŸ-˜-J˜?J˜J˜$Jšœ˜J˜—˜*J™J˜"J˜—J˜˜ÈJ˜—˜vJ˜—Jšœ˜J˜J˜—…—4*8