WalnuthatchImpl.mesa
Last Edited by: Swinehart, October 17, 1985 12:20:30 pm PDT
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; 
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← 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;
Call Finch Recorder, get back tune data. Compose VoiceFileID from tune number and time.
[] ← procs.playNoise[noiseName: $beep, failOK: TRUE];
reported intervals will be ignored by smarts?
[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�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: nuthatchUserHandle.defaultType
];
};
StopProc, StopPlayProc: ENTRY Menus.MenuProc = {
ENABLE UNWIND => NULL;
IF StartFinch[FALSE]#running THEN RETURN;
procs.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 handle. --
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] = {
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];
};
StartFinch: INTERNAL PROC[complain: BOOLTRUE]
RETURNS [state: FinchSmarts.FinchState] = {
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[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[];
};
Rollback hack.
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[];
};
End of Rollback hack.
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 = {
HACK HACK HACK HACK HACK HACK!
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.