DIRECTORY FinchSmarts USING [FinchIsRunning, PlaybackTune, RecordReason, RecordTune, StopTune], GVBasics USING [RName], IO, Log USING [Report], Menus USING [MenuProc], Nuthatch USING [AddInterest, CatalogVoiceFile, defaultType, EncryptionKey, GetDirectoryEntry, GetFileID, ID, InitializeNuthatch, LoseInterest, NuthatchUserHandle, Tune, VoiceFileID, VoiceInterval], NuthatchImpl, PrincOpsUtils USING [ IsBound ], Process USING [Detach], RecordingLock USING [GetLock, ReleaseLock], Rope USING [Equal, ROPE], BasicTime USING [Now, GMT], ViewerClasses USING [Viewer], ViewerOps USING [FetchProp], WalnutDisplayerOps USING [AddToMsgMenu, GetMsgName], Walnuthatch, WalnuthatchMonitorImpl, WalnutSendInternal USING [SenderReport], WalnutSendOps USING [AddToSendMenu, AppendHeaderLine]; WalnuthatchSendImpl: CEDAR MONITOR LOCKS root IMPORTS root: WalnuthatchMonitorImpl, FinchSmarts, IO, Log, Nuthatch, NuthatchImpl, PrincOpsUtils, Process, RecordingLock, Rope, BasicTime, 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; }; InitializeWalnuthatch: PUBLIC PROC [userName: GVBasics.RName_NIL] RETURNS [success: BOOL, nuthatchUserHandle: Nuthatch.NuthatchUserHandle] = { [success, nuthatchUserHandle] _ Nuthatch.InitializeNuthatch[userName: userName, RefIDType: "GVID"]; IF NOT success THEN { Log.Report["Problem with nuthatch database or log!", $Finch]; RETURN; }; -- 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] }; END. ˜WalnuthatchSendImpl.mesa Last Edited by: Swinehart, April 26, 1984 6:17:40 pm PST 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. ʪ˜J™J™8J™šÏk ˜ Jšœ œD˜UJšœ œ ˜Jšœ˜Jšœœ ˜Jšœœ ˜Jšœ œ[œZ˜ÅJ˜ Jšœœ ˜ Jšœœ ˜Jšœœ˜+Jšœœ œ˜Jšœ œœ˜Jšœœ ˜Jšœ œ ˜Jšœœ˜4J˜ J˜Jšœœ˜(Jšœœ#˜6J˜—šœœœœ˜.š˜J˜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˜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˜—šžœœœœ˜AJšœ œ6˜L˜J˜C—šœœ œ˜Jšœ>œ˜H—JšŸ-˜-J˜?J˜AJ˜?J˜CJ˜J˜?J˜GJ˜IJ˜LJšœ˜J˜—Jšœ˜J˜J˜—…—j&¬