WalnuthatchImpl.mesa
Last Edited by: Swinehart, April 30, 1984 2:12:24 pm PDT
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; 
Try to grab the right to record. If possible, go on and do it. If not, report failure to the caller.
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]];
Hack! Hack! nuthatchUserHandle←NuthatchImpl.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;
Call Finch Recorder, get back tune data. Compose VoiceFileID from tune number and time.
[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�sicTime.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 = {
Add a line to the Sender in which a VoiceFileID can be inserted. --
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];
Get tune, interval, and key of the most recently recorded voice from NuthatchImpl frame. --
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]];
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. --
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]];
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. --
Nuthatch.LoseInterest[nuthatchUserHandle: nuthatchUserHandle, refID: GVID, time: time];
};
MoveTo: PUBLIC ENTRY PROC [
msgset: Rope.ROPE, nuthatchUserHandle: Nuthatch.NuthatchUserHandle, RefID: Nuthatch.ID] = {
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?
ENABLE UNWIND => NULL;
time: BasicTime.GMT;
IF (Rope.Equal[msgset, "deleted",FALSE] OR Rope.Equal[msgset, "active", FALSE]) THEN RETURN;
time�sicTime.Now[];
Nuthatch.AddInterest[nuthatchUserHandle: nuthatchUserHandle, refID: RefID, time: time];
};
FinchState: TYPE = { notLoaded, notStarted, running };
StartFinch: INTERNAL PROC[complain: BOOLTRUE] RETURNS [state: FinchState←running] = {
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.
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;
};
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.
rollOn: CONDITION;
realRollbackButtonProc: Buttons.ButtonProc ← NIL;
reallyRoll: BOOLTRUE;
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];
};
PlayTheRollbackCommand: Buttons.ButtonProc = {
IF realRollback=NIL THEN RETURN; -- serious problem!
PlayTheRollback[];
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];
};
End of Rollback hack.
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.