DIRECTORY
Commander USING [ CommandProc, Register ],
CommandTool USING [ NextArgument ],
Intervoice USING [ Forget, Handle, Open, Play, Record, Retain, Stop ],
IO,
MBQueue USING [ Create ],
Menus USING [MenuProc],
Process USING [ Detach ],
RefText USING [ ObtainScratch, TrustTextAsRope],
Rope USING [Cat, Equal, Fetch, Find, Length, MakeRope, ROPE, Substr],
TiogaOps USING [ Location, GetRope, GetSelection, ViewerDoc ],
ViewerClasses USING [Viewer],
VoiceUtils USING [ CurrentRName ],
WalnutDefs USING [ Error ],
WalnutDocumentRope USING [ Create ],
WalnutOps USING [ GetMsgHeaders, MsgHeaders,ParseHeaders, ParseProc ],
WalnutRegistry USING [ MoveProc, MsgGroupProc, MsgGroupSize, Register ],
WalnutSendInternal USING [SenderReport],
WalnutSendOps USING [AddToSendMenu, AppendHeaderLine],
WalnutWindow USING [ AddToMsgMenu, GetMsgName ]
;
Procedures registered with WalnutRegistry
VoiceMoveTo:
ENTRY WalnutRegistry.MoveProc = {
[msgName: ROPE, fromMsgSet: ROPE, toMsgSet: ROPE, clientData: REF ANY]
ENABLE { ABORTED => CONTINUE; UNWIND => NULL; };
headers: WalnutOps.MsgHeaders;
IF voiceHandle=NIL OR toMsgSet=NIL THEN RETURN;
IF (Rope.Equal[toMsgSet, "deleted",FALSE] OR Rope.Equal[toMsgSet, "active", FALSE]) THEN RETURN;
headers ← VoiceHdrsFromGVID[msgName];
FOR hdrs: WalnutOps.MsgHeaders ← headers, hdrs.rest
WHILE hdrs#
NIL
DO
[]←Intervoice.Retain[
handle: voiceHandle, voiceFileID: hdrs.first.value,
refID: Rid[msgName], refIDType: "GVID"];
ENDLOOP;
};
ChangeInterestEntry:
ENTRY WalnutRegistry.MsgGroupProc = {
[msgGroup: REF WalnutRegistry.MsgGroup, event: WalnutRegistry.MsgGroupEvent, clientData: REF ANY]
ENABLE { ABORTED => CONTINUE; UNWIND => NULL; };
IF voiceHandle = NIL THEN RETURN;
FOR i:
CARDINAL
IN [0..WalnutRegistry.MsgGroupSize)
DO
IF msgGroup[i]=NIL THEN LOOP;
SELECT event
FROM
added => NULL; -- Present design calls for no action at message read time.
destroyed => Intervoice.Forget[voiceHandle, Rid[msgGroup[i]], "GVID"];
ENDCASE;
ENDLOOP;
};
Utilities, Available from elsewhere
PlayFromSelection:
INTERNAL
PROC[parentViewer: ViewerClasses.Viewer]
RETURNS [played: BOOL←FALSE] = {
parentViewer is viewer containing Play menu entry. If selection is a VoiceFileID node (wherever located), play that and return TRUE. Otherwise ...
selV: ViewerClasses.Viewer;
selStart, selEnd: TiogaOps.Location;
nodeContents: Rope.ROPE;
vfIndex: INT←-1;
[selV, selStart, selEnd] ← TiogaOps.GetSelection[primary];
IF selV#parentViewer OR selStart.node#selEnd.node THEN RETURN;
nodeContents ← TiogaOps.GetRope[selStart.node];
IF (vfIndex←nodeContents.Find["VoiceFileID: "])<0 THEN RETURN;
Intervoice.Play[handle: voiceHandle, voiceFileID: nodeContents.Substr[start: 14]];
RETURN[TRUE];
};
PlayFromMessageHeaders:
INTERNAL
PROC[headers: WalnutOps.MsgHeaders] = {
Play in reverse order (observe clever recursion), to reverse the effect of ParseHeaders.
IF headers=NIL THEN RETURN;
PlayFromMessageHeaders[headers.rest];
Intervoice.Play[handle: voiceHandle, voiceFileID: headers.first.value];
};
VoiceHdrsFromGVID:
INTERNAL PROC[msgID: Rope.ROPE] RETURNS [WalnutOps.MsgHeaders] = {
msgText ← WalnutOps.GetMsgHeaders[msgID, msgText!
WalnutDefs.Error => IF who=$log AND code=$MsgHeadersTooLong THEN msgText.length𡤀];
RETURN[VoiceHdrsFromMsg[RefText.TrustTextAsRope[msgText]]];
};
VoiceHdrsFromMsg:
INTERNAL
PROC[msgHeaders: Rope.
ROPE]
RETURNS [WalnutOps.MsgHeaders] = {
RETURN[
IF msgHeaders.Length[]=0
THEN
NIL
ELSE
WalnutOps.ParseHeaders[msgHeaders, FindVoiceID]];
};
FindVoiceID: WalnutOps.ParseProc = {
[fieldName: ROPE] RETURNS [wantThisOne: BOOL, continue: BOOL]
RETURN[fieldName.Equal["voicefileid", FALSE], TRUE];
};
LowerCaseRope:
PROC[r:
ROPE]
RETURNS [
ROPE] = {
RETURN[Rope.MakeRope[base: r, size: r.Length[], fetch: LCFetch]]};
LCFetch:
SAFE
PROC[data:
REF, index:
INT]
RETURNS [c:
CHAR] =
TRUSTED {
SELECT (c←NARROW[data,ROPE].Fetch[index]) FROM IN ['A..'Z]=>c𡤌+('a-'A); ENDCASE};
Rid:
PROC[msgHeader: Rope.
ROPE]
RETURNS [dbRidValue: Rope.
ROPE] = {
RETURN[Rope.Cat[LowerCaseRope[VoiceUtils.CurrentRName[]], "/", msgHeader]];
};
WalnutComplain:
PROC[complaint: Rope.
ROPE] = {
WalnutSendInternal.SenderReport[complaint]; };
Initialization (Walnut Voice and Finch)
InitializeWalnutVoice:
Commander.CommandProc = {
tunesDBName: Rope.ROPE ← CommandTool.NextArgument[cmd];
instance: Rope.ROPE ← CommandTool.NextArgument[cmd];
voiceHandle ← Intervoice.Open[
tunesDBName: tunesDBName, tunesDBInstance: instance, Complain: WalnutComplain];
Set up the button procs needed by Walnut.
WalnutSendOps.AddToSendMenu[label: "Record", proc: RecordProc];
WalnutSendOps.AddToSendMenu[label: "STOP!", proc: StopProc];
WalnutSendOps.AddToSendMenu[label: "Play", proc: SenderPlayProc];
WalnutSendOps.AddToSendMenu[label: "Attach", proc: AttachProc];
WalnutWindow.AddToMsgMenu[label: "Play", proc: MessagePlayProc];
WalnutWindow.AddToMsgMenu[label: "STOP!", proc: StopProc];
[]←WalnutRegistry.Register[
[
msgGroupProc: ChangeInterestEntry,
msgGroupData: NIL, -- used to be handle
moveProc: VoiceMoveTo,
moveProcData: NIL -- used to be handle
],
MBQueue.Create[]
];
};
Commander.Register["WalnutVoice", InitializeWalnutVoice,
"WalnutVoice [<tunes db root>] [Instance]\nRegister WalnutVoice with walnut, extend Walnut menus."];
}.
Swinehart, January 30, 1986 3:47:33 pm PST
Now uses Intervoice to manage recording and playback.
changes to: voiceHandle, RecordProc, DoRecordProc, StopProc, PlayFromSelection, PlayFromMessageHeaders, FindVoiceID, WalnutComplain, InitializeWalnutVoice