DIRECTORY Buttons USING [ ButtonProc ], FinchSmarts USING [FinchIsRunning, PlaybackTune, RecordReason, RecordTune, StopTune], GVBasics USING [RName], IO, Log USING [Report], Menus USING [MenuProc], Nuthatch USING [AddInterest, CatalogVoiceFile, currentNUH, defaultType, EncryptionKey, GetDirectoryEntry, GetFileID, ID, InitializeNuthatch, LoseInterest, NuthatchUserHandle, NuthatchUserRec, Tune, VoiceFileID, VoiceInterval], NuthatchImpl, PrincOpsUtils USING [ IsBound ], Process USING [Detach, Pause, SetTimeout, SecondsToTicks], RecordingLock USING [GetLock, ReleaseLock], Rope USING [Equal, ROPE], BasicTime USING [Now, GMT], Unclean USING [ SwapQueuedButtonProcs ], UserProfile USING [ Token ], ViewerClasses USING [Viewer], ViewerOps USING [FetchProp], WalnutDisplayerOps USING [AddToMsgMenu, GetMsgName], Walnuthatch, WalnutSendInternal USING [SenderReport], WalnutSendOps USING [AddToSendMenu, AppendHeaderLine]; WalnuthatchImpl: CEDAR MONITOR IMPORTS FinchSmarts, IO, Log, Nuthatch, NuthatchImpl, PrincOpsUtils, Process, RecordingLock, Rope, BasicTime, Unclean, UserProfile, ViewerOps, WalnutDisplayerOps, WalnutSendInternal, WalnutSendOps EXPORTS Walnuthatch = BEGIN OPEN Walnuthatch; 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_ NARROW[ViewerOps.FetchProp[viewer, $NuthatchHandle]]; 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; [reason, tune, [startSample, samples], key] _ FinchSmarts.RecordTune[]; RecordingLock.ReleaseLock[]; NuthatchImpl.tune_ tune; NuthatchImpl.startSample_ startSample; NuthatchImpl.samples_ samples; NuthatchImpl.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: Nuthatch.defaultType ]; }; StopProc, StopPlayProc: ENTRY Menus.MenuProc = { ENABLE UNWIND => NULL; IF StartFinch[FALSE]#running THEN RETURN; FinchSmarts.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_NuthatchImpl.tune; samples_NuthatchImpl.samples; startSample_NuthatchImpl.startSample; key_NuthatchImpl.key; interval _ []; FinchSmarts.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_ WalnutDisplayerOps.GetMsgName[viewer]; nuthatchUserHandle_ NARROW[ViewerOps.FetchProp[viewer, $NuthatchHandle]]; voiceFileID _ Nuthatch.GetFileID[GVID, nuthatchUserHandle]; [tune, , , , , , key, , , found] _ Nuthatch.GetDirectoryEntry[voiceFileID]; IF found THEN FinchSmarts.PlaybackTune[tune: tune, interval: [], key: key]; }; KeepVoiceProc: ENTRY Menus.MenuProc = { ENABLE UNWIND => NULL; nuthatchUserHandle: Nuthatch.NuthatchUserHandle; viewer: ViewerClasses.Viewer_ NARROW[parent]; time: BasicTime.GMT_ BasicTime.Now[]; GVID: Rope.ROPE_ WalnutDisplayerOps.GetMsgName[viewer]; -- need this here?? -- nuthatchUserHandle_NARROW[ViewerOps.FetchProp[viewer, $NuthatchHandle]]; Nuthatch.AddInterest[nuthatchUserHandle: nuthatchUserHandle, refID: GVID, time: time]; }; RemoveVoiceProc: ENTRY Menus.MenuProc = { ENABLE UNWIND => NULL; nuthatchUserHandle: Nuthatch.NuthatchUserHandle; viewer: ViewerClasses.Viewer_ NARROW[parent]; time: BasicTime.GMT_ BasicTime.Now[]; GVID: Rope.ROPE_ WalnutDisplayerOps.GetMsgName[viewer]; nuthatchUserHandle_ NARROW[ViewerOps.FetchProp[viewer, $NuthatchHandle]]; Nuthatch.LoseInterest[nuthatchUserHandle: nuthatchUserHandle, refID: GVID, time: time]; }; 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]; }; FinchState: TYPE = { notLoaded, notStarted, running }; StartFinch: INTERNAL PROC[complain: BOOL_TRUE] RETURNS [state: FinchState_running] = { RW: PROC[s: FinchState, c: Rope.ROPE] RETURNS [state: FinchState] = { state_s; IF ~complain THEN RETURN; WalnutSendInternal.SenderReport[msg: c]; }; SELECT TRUE FROM ~PrincOpsUtils.IsBound[FinchSmarts.FinchIsRunning] => state_RW[notLoaded, "Sorry, Finch needs to be loaded and started.\n"]; ~FinchSmarts.FinchIsRunning[] => state_RW[notLoaded, "Sorry, Finch needs to be connected to telephone server.\nUse \"Finch\" command or push \"Participate\" button in the Finch viewer.\n"]; ENDCASE; }; rollOn: CONDITION; realRollbackButtonProc: Buttons.ButtonProc _ NIL; reallyRoll: BOOL_TRUE; rollbackKey: Nuthatch.EncryptionKey; rollbackTune: Nuthatch.Tune; rollTuneFound: BOOL; PlayTheRollbackButton: Buttons.ButtonProc = { IF realRollbackButtonProc=NIL THEN RETURN; -- serious problem! PlayTheRollback[]; IF reallyRoll THEN realRollbackButtonProc[parent, clientData, mouseButton, control]; }; PlayTheRollback: ENTRY PROC = TRUSTED { ENABLE ANY=>CONTINUE; IF StartFinch[FALSE]#running OR Nuthatch.currentNUH=NIL THEN RETURN; Process.SetTimeout[@rollOn, Process.SecondsToTicks[10]]; Process.Detach[FORK ReallyPlayTheRollback[]]; WAIT rollOn; }; ReallyPlayTheRollback: ENTRY PROC = { ENABLE ANY=>CONTINUE; IF StartFinch[FALSE]#running OR Nuthatch.currentNUH=NIL THEN RETURN; IF rollTuneFound THEN { FinchSmarts.PlaybackTune[tune: rollbackTune, interval: [], key: rollbackKey]; Process.Pause[Process.SecondsToTicks[2]]; }; NOTIFY rollOn; }; GetSysNoises: ENTRY PROC[nuthatchUserHandle: Nuthatch.NuthatchUserHandle] = { ENABLE ANY=>CONTINUE; nuh: Nuthatch.NuthatchUserHandle; voiceFileID: Nuthatch.VoiceFileID; IF nuthatchUserHandle=NIL THEN RETURN; nuh _ NEW[Nuthatch.NuthatchUserRec _ nuthatchUserHandle^]; nuh.userName _ UserProfile.Token[key: "ThrushServerInstance", default: "Morley.Lark"]; nuh.refIDType _ "SysNoises"; voiceFileID _ Nuthatch.GetFileID["Rollback", nuh]; IF voiceFileID = NIL THEN RETURN; [rollbackTune, , , , , , rollbackKey, , , rollTuneFound] _ Nuthatch.GetDirectoryEntry[voiceFileID]; }; InitializeWalnuthatch: PUBLIC PROC [userName: GVBasics.RName_NIL] RETURNS [success: BOOL, nuthatchUserHandle: Nuthatch.NuthatchUserHandle] = { [success, nuthatchUserHandle] _ Nuthatch.InitializeNuthatch[userName: userName, RefIDType: "GVID", close: FALSE]; IF NOT success THEN { Log.Report["Problem with nuthatch database or log!", $Finch]; RETURN; }; GetSysNoises[nuthatchUserHandle]; -- HACK HACK HACK HACK -- Sets up the button procs needed by Walnut. WalnutSendOps.AddToSendMenu[label: "Record", proc: RecordProc]; WalnutSendOps.AddToSendMenu[label: "StopRecord", proc: StopProc]; WalnutSendOps.AddToSendMenu[label: "Attach", proc: AttachProc]; WalnutSendOps.AddToSendMenu[label: "Playback", proc: PlaybackProc]; WalnutDisplayerOps.AddToMsgMenu[label: "Play", proc: PlayProc]; WalnutDisplayerOps.AddToMsgMenu[label: "StopPlay", proc: StopPlayProc]; WalnutDisplayerOps.AddToMsgMenu[label: "KeepVoice", proc: KeepVoiceProc]; WalnutDisplayerOps.AddToMsgMenu[label: "RemoveVoice", proc: RemoveVoiceProc]; IF realRollbackButtonProc = NIL THEN realRollbackButtonProc _ Unclean.SwapQueuedButtonProcs["Rollback", PlayTheRollbackButton]; }; END. øWalnuthatchImpl.mesa Last Edited by: Swinehart, April 30, 1984 2:12:24 pm PDT Try to grab the right to record. If possible, go on and do it. If not, report failure to the caller. Hack! Hack! nuthatchUserHandle_NuthatchImpl.currentNUH; Call Finch Recorder, get back tune data. Compose VoiceFileID from tune number and time. 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 NuthatchImpl frame. -- Add an interest entry for this user, refidtype, refid, and voicemessageid, if there isn't one already (which there should be) and set its interest field to true. -- Find the interest entry for this user, refidtype, refid, and voicemessageid, (there should be one but may no longer be???) and set its interest field to false. -- 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. This stuff all belongs one level up. Investigate sometime what would be required to make it as good that way as it is this way. Rollback hack. PlayTheRollbackCommand: Buttons.ButtonProc = { IF realRollback=NIL THEN RETURN; -- serious problem! PlayTheRollback[]; realRollbackButtonProc[parent, clientData, mouseButton, control]; }; End of Rollback hack. �ÊߘJ™J™8J™šÏk ˜ Jšœ�œ˜Jšœ �œD˜UJšœ �œ ˜Jš�œ˜Jšœ�œ ˜Jšœ�œ ˜Jšœ �œg�œk˜âJ˜ Jšœ�œ ˜ Jšœ�œ-˜:Jšœ�œ˜+Jšœ�œ �œ˜Jšœ �œ�œ˜Jšœ�œ˜(Jšœ �œ ˜Jšœ�œ ˜Jšœ �œ ˜Jšœ�œ˜4J˜ Jšœ�œ˜(Jšœ�œ#˜6J˜—šœ�œ�œ˜š�˜Jšœ �œ­˜¼—Jš�œ˜Jš�œ�œ ˜J˜J˜—šÏn œ�œ�˜%Jš�œ�œ�œ˜Jšœ�œ ˜.J˜1Jšœf™fJš�œ�œ�œ˜$Jš�œ�œ�œ˜)š�Ïc]˜`Jšœ�œ/˜IJšœ8™8Jšœ�œ,˜?J˜—Jš�œ=˜AJšœ˜—J˜šž œ�œ˜Jšœ0˜0Jšœ!˜!Jš�œ�œ�œ˜Jšœ �œ˜Jšœ �œ˜ J˜J˜!J˜J˜J˜"J˜JšœX™X—˜JšœH˜HJ˜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šœ[™[Jš�œ�œ�œ˜$J˜J˜J˜%J˜J˜J˜CJšœ˜J˜—šžœ�œŸ)˜LJš�œ�œ�œ˜Jšœ�œ˜ Jš�œ�œ˜J˜J˜0J˜Jšœ�œ ˜-J˜"J˜Jš�œ�œ�œ˜$Jš�œ(˜,Jšœ�œ/˜IJšœ!�œ˜;J˜KJš�œ�œ>˜KJšœ˜J˜—J˜šž œ�œ˜'Jš�œ�œ�œ˜J˜0Jšœ�œ ˜-J˜%Jš�œ�œ)Ÿ˜NJ˜Jšœ�œ/˜HJšœ¥™¥JšœD�œ˜VJšœ˜—J˜šÏbœ�œ˜)Jš�œ�œ�œ˜J˜0Jšœ�œ ˜-J˜%Jš�œ�œ(˜7Jšœ�œ/˜IJšœ£™£JšœE�œ˜WJšœ˜—J˜šžœ�œ�œ�œ˜Jšœ �œC�œ˜[JšœÇ™ÇJš�œ�œ�œ˜J˜Jš �œ�œ�œ�œ� œ˜]J˜J˜WJ˜—J˜J˜6J˜š ž œ�œ�œ �œ�œ�œ ˜VJ™DJ™Nš�œ�œ�œ�œ˜EJ˜Jš�œ �œ�œ˜Jšœ(˜(J˜—š�œ�œ�˜˜5Jšœ�œ>˜F—˜ Jšœ�œ”˜œ—Jš�œ˜—J˜—J˜J™�Jšœ� œ˜Jšœ-�œ˜1Jšœ �œ�œ˜J˜$Jšœ�˜Jšœ�œ˜J˜šžœ˜-Jš �œ�œ�œ�œŸ˜>J˜Jš�œ �œB˜TJ˜J˜—šžœ™.Jš �œ�œ�œ�œŸ™4J™JšœA™AJ™J™—šžœ�œ�œ�œ˜'Jš�œ�œ�œ˜Jš �œ �œ �œ�œ�œ�œ˜DJ˜8Jšœ�œ˜-Jš�œ˜ J˜—J˜šžœ� œ˜%Jš�œ�œ�œ˜Jš �œ �œ �œ�œ�œ�œ˜Dš�œ�œ˜JšœM˜MJ˜)J˜—Jš�œ˜J˜—J˜šž œ�œ�œ5˜MJš�œ�œ�œ˜Jšœ!˜!J˜"Jš�œ�œ�œ�œ˜&Jšœ�œ1˜:JšœV˜VJ˜J˜2Jš�œ�œ�œ�œ˜!˜:J˜(—J˜—J™J˜šžœ�œ�œ�œ˜AJš�œ �œ6˜L˜JšœJ�œ˜Q—š�œ�œ �œ˜Jšœ>�œ˜H—Jšœ!Ÿ˜8JšŸ-˜-J˜?J˜AJ˜?J˜CJ˜J˜?J˜GJ˜IJ˜Mš�œ�œ�œ˜=JšœA˜A—Jšœ˜J˜—Jš�œ˜J˜J˜—…—"”1k