DIRECTORY
FinchSmarts USING [CurrentFinchState, FinchState, GetProcs, Procs],
FS USING [ComponentPositions, ExpandName],
Rope USING [Cat, Replace, ROPE],
Thrush USING [EncryptionKey, NB],
UserProfile USING [Token],
VoiceTemp USING [ TuneID, IntervalSpec, IntervalSpecs, IntervalSpecBody ],
VoiceUtils USING [Problem, RnameToRspec],
VoiceRopeDB,
VoiceRope;
Intervoice operations (record and play)
The following routines were adapted from RecordPlayImpl.mesa
Open:
PUBLIC
PROC[voiceRopeDBName: Rope.
ROPE ←
NIL, voiceRopeDBInstance: Rope.
ROPE ←
NIL, localName: Rope.
ROPE ←
NIL, Complain:
PROC[complaint: Rope.
ROPE]←
NIL]
RETURNS [handle: Handle] = {
If voiceRopeDBName, voiceRopeDBInstance, or localName is omitted, a default based on the user profile choice of Thrush Server is invented. If Complain is omitted, VoiceUtils.Problem[...$Finch] is used.
ENABLE VoiceRopeDB.Error => {
-- Trouble opening; success of future calls in doubt
handle.Complain[explanation];
CONTINUE;
};
server: Rope.ROPE ← UserProfile.Token["ThrushClientServerInstance", "Strowger.Lark"];
vdbHandle: VoiceRopeDB.Handle;
remoteDBName: ROPE;
cp: FS.ComponentPositions;
default parameters
IF Complain = NIL THEN Complain ← FinchProblem;
IF voiceRopeDBName =
NIL
THEN
voiceRopeDBName ← Rope.Cat["/", server, "//", VoiceUtils.RnameToRspec[server].simpleName, "/VoiceRopeDB"];
put instance in db name
set up handle
vdbHandle ← VoiceRopeDB.Open[voiceRopeDBName];
handle ← NEW[HandleRec ← [vdbHandle: vdbHandle, Complain: Complain]];
};
FinchProblem:
PROC[complaint: Rope.
ROPE] = { VoiceUtils.Problem[complaint, $Finch]; };
StartFinch:
PROC[handle: Handle, complain:
BOOL←
TRUE]
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;
handle.Complain[complaint: 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.\n"];
running => NULL;
ENDCASE => ERROR;
handle.procs ← IF state=unknown THEN NIL ELSE FinchSmarts.GetProcs[];
};
ValidateHandle:
PROC[oldHandle: Handle]
RETURNS [handle: Handle] = {
handle ← oldHandle;
IF handle=
NIL
THEN {
IF defaultHandle=NIL THEN defaultHandle ← Open[];
handle ← defaultHandle;
};
};
Record:
PUBLIC
PROC[handle: Handle ←
NIL]
RETURNS [voiceRope: VoiceRope] = {
Records a voice rope, registers it , and returns its ID. A NIL return value indicates that something went wrong.
ENABLE VoiceRopeDB.Error => { handle.Complain[explanation];
CONTINUE; };
Nothing much can be done about it; just tell the user. Should eventually log for system administrator's benefit.
intervalSpec: VoiceTemp.IntervalSpec;
key: Thrush.EncryptionKey;
nb: Thrush.NB;
handle ← ValidateHandle[handle];
IF StartFinch[handle]#running THEN RETURN;
I don't know how you decided to name and lookup SysNoises
Play[handle: handle, refID: "BeepTune", refIDType: "SysNoises", failOK: TRUE];
[nb, intervalSpec, key, handle.convID] ← handle.procs.recordTune[convID: handle.convID, queueIt: TRUE];
IF nb#$success THEN RETURN;
Can't clip the rope to remove surrounding silences, because of the matching screen stuff. See /voice/voice/dcs/top/thrush.df/recordplayimpl.mesa for an example of that.
IF intervalSpec.length = -1
THEN
intervalSpec.length ← handle.procs.describeInterval[intervalSpec].length;
voiceRope ← NEW[VoiceRopeInterval ← [ropeID: NIL, start: 0, length: -1]];
[voiceRope.ropeID, voiceRope.length] ← VoiceRopeDB.Write[handle: handle.vdbHandle, struct: VoiceRopeDB.SimpleTuneList[intervalSpec.tuneID, intervalSpec.start, intervalSpec.length, key]];
};
Currently, the Play routine can only play a voice rope given its ID; the refID and refIDType fields are missing. How to manage system noises, like Beeps and intolerably cute rollback tunes, in the context of voice ropes needs to be resolved.
Play:
PUBLIC
PROC[handle: Handle←
NIL, voiceRope: VoiceRope, queueIt:
BOOL←
TRUE, failOK:
BOOL←
FALSE] = {
Play a specified voice rope. The boolean arguments are interpreted as follows:
queueIt => play after all other record/playback requests are satisfied.
failOK => playing is optional; leave connection open if tune doesn't exist.
wait => wait until things appear to be started properly, or have failed.
ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; };
tuneID: VoiceTemp.TuneID;
start, length: INT;
intervalSpec: VoiceTemp.IntervalSpec;
key: Thrush.EncryptionKey;
struct: VoiceRopeDB.TuneList;
handle ← ValidateHandle[handle];
IF StartFinch[handle]#running THEN RETURN;
struct ← ReadVoiceRope[handle, voiceRope];
IF struct=NIL THEN { handle.Complain["No such voice rope to play."]; RETURN;};
UNTIL struct=
NIL
DO
[tuneID, start, length, key, struct] ← VoiceRopeDB.NextTuneOnList[struct];
intervalSpec ← NEW[VoiceTemp.IntervalSpecBody ← [tuneID, start, length]];
handle.convID←handle.procs.playbackTune[intervalSpec: intervalSpec, key: key, queueIt: queueIt, failOK: failOK, convID: handle.convID].newConvID;
queueIt ← TRUE;
ENDLOOP;
};
Stop:
PUBLIC
PROC[handle: Handle ←
NIL] = {
Sender and Walnut Message viewer STOP buttons
ENABLE UNWIND => NULL;
handle ← ValidateHandle[handle];
IF StartFinch[handle, FALSE]#running THEN RETURN;
handle.procs.stopTune[handle.convID];
};
Editing operations
Cat:
PUBLIC
PROC [handle: Handle ←
NIL, vr1, vr2, vr3, vr4, vr5: VoiceRope ←
NIL]
RETURNS [new: VoiceRope] ~ {
Concatenates together the non-NIL voice ropes to produce a new voice rope.
ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; };
struct: VoiceRopeDB.TuneList;
IF vr1 = NIL THEN RETURN[NIL];
handle ← ValidateHandle[handle];
struct ← ReadVoiceRope[handle, vr1];
IF vr2 #
NIL
THEN
struct ← CatEntries[struct, ReadVoiceRope[handle, vr2]];
IF vr3 #
NIL
THEN
struct ← CatEntries[struct, ReadVoiceRope[handle, vr3]];
IF vr4 #
NIL
THEN
struct ← CatEntries[struct, ReadVoiceRope[handle, vr4]];
IF vr5 #
NIL
THEN
struct ← CatEntries[struct, ReadVoiceRope[handle, vr5]];
new ← NEW[VoiceRopeInterval ← [ropeID: NIL, start: 0, length: -1]];
[new.ropeID, new.length] ← VoiceRopeDB.Write[handle.vdbHandle, struct];
};
Substr:
PUBLIC
PROC [handle: Handle ←
NIL, vr: VoiceRope, start:
INT ← 0, len:
INT ←
LAST[
INT]]
RETURNS [new: VoiceRope] ~ {
Creates a new voice rope that is a substring of an existing voice rope.
ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; };
struct: VoiceRopeDB.TuneList;
handle ← ValidateHandle[handle];
struct ← ReadVoiceRope[handle, vr];
IF struct=NIL THEN { handle.Complain["No such voice rope."]; RETURN[NIL];};
struct ← VoiceRopeDB.TuneListInterval[struct, start, len];
new ← NEW[VoiceRopeInterval ← [ropeID: NIL, start: 0, length: -1]];
[new.ropeID, new.length] ← VoiceRopeDB.Write[handle.vdbHandle, struct];
};
Replace:
PUBLIC
PROC [handle: Handle ←
NIL, vr: VoiceRope, start:
INT ← 0, len:
INT ←
LAST[
INT], with: VoiceRope ←
NIL]
RETURNS [new: VoiceRope] ~ {
Creates a new voice rope in which the given interval of the voice rope "vr" is replaced by the voice rope "with".
ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; };
base, struct: VoiceRopeDB.TuneList;
handle ← ValidateHandle[handle];
base ← ReadVoiceRope[handle, vr];
IF base=NIL THEN { handle.Complain["No such voice rope."]; RETURN[NIL];};
struct ← VoiceRopeDB.TuneListInterval[CopyEntry[base], 0, start];
struct ← CatEntries[struct, ReadVoiceRope[handle, with]];
struct ← CatEntries[struct, VoiceRopeDB.TuneListInterval[base, start+len, LAST[INT]]];
new ← NEW[VoiceRopeInterval ← [ropeID: NIL, start: 0, length: -1]];
[new.ropeID, new.length] ← VoiceRopeDB.Write[handle.vdbHandle, struct];
};
Length:
PUBLIC
PROC [handle: Handle ←
NIL, vr: VoiceRope]
RETURNS [len:
INT] ~ {
Returns the actual length of the voice rope. This operation ignores the start and length values specified in the voice rope. Thus, vr.start ← 0; vr.length ← Length[handle, vr] will restore a voice rope to its full contents.
ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; };
handle ← ValidateHandle[handle];
len ← VoiceRopeDB.Read[handle.vdbHandle, vr.ropeID].length;
};
Information about voice ropes
DescribeRope:
PUBLIC PROC [handle: Handle ←
NIL, vr: VoiceRope, minSilence:
INT ← -1]
RETURNS [noise: IntervalSpecs] ~ {
ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; };
struct: VoiceRopeDB.TuneList;
tuneSpec: VoiceTemp.IntervalSpecs;
tuneID: VoiceTemp.TuneID;
start, length: INT;
noiseEnd: IntervalSpecs ← NIL; -- last item on current list of noise intervals
samples: INT ← 0;
handle ← ValidateHandle[handle];
IF StartFinch[handle]#running THEN RETURN[NIL]; -- Need some error reporting??
struct ← ReadVoiceRope[handle, vr];
UNTIL struct=
NIL
DO
intervalSpec: VoiceTemp.IntervalSpec;
[tuneID: tuneID, start: start, length: length, rest: struct] ← VoiceRopeDB.NextTuneOnList[struct];
intervalSpec ← NEW[VoiceTemp.IntervalSpecBody ← [tuneID, start, length]];
tuneSpec ← handle.procs.describeInterval[intervalSpec: intervalSpec, minSilence: minSilence].intervals;
UNTIL tuneSpec =
NIL
DO
IF noiseEnd =
NIL
THEN {
noise ← LIST[[start: samples + tuneSpec.first.start - intervalSpec.start, length: tuneSpec.first.length]];
noiseEnd ← noise;
}
ELSE {
noiseEnd.rest ← LIST[[start: samples + tuneSpec.first.start - intervalSpec.start, length: tuneSpec.first.length]];
noiseEnd ← noiseEnd.rest;
};
tuneSpec ← tuneSpec.rest;
ENDLOOP;
samples ← samples + intervalSpec.length;
ENDLOOP;
};
Fetch: PUBLIC PROC [handle: Handle ← NIL, vr: VoiceRope, index: INT] RETURNS [VoiceSample];
Miscellaneous
ReadVoiceRope:
PROC [handle: Handle, vr: VoiceRope]
RETURNS [struct: VoiceRopeDB.TuneList] ~ {
length: INT;
[length: length, struct: struct] ← VoiceRopeDB.Read[handle.vdbHandle, vr.ropeID];
IF
NOT (vr.start = 0
AND vr.length = length)
THEN
struct ← VoiceRopeDB.TuneListInterval[struct, vr.start, vr.length];
};
CatEntries:
PROC [e1, e2: VoiceRopeDB.TuneList]
RETURNS [VoiceRopeDB.TuneList] ~ {
Concatenates the two lists together (destructive to the first).
ptr: VoiceRopeDB.TuneList ← e1;
IF ptr = NIL THEN RETURN[e2];
UNTIL ptr.rest =
NIL
DO
ptr ← ptr.rest;
ENDLOOP;
ptr.rest ← e2;
RETURN[e1];
};
CopyEntry:
PROC [entry: VoiceRopeDB.TuneList]
RETURNS [VoiceRopeDB.TuneList] ~ {
Copies one list to another.
new, end: VoiceRopeDB.TuneList;
IF entry = NIL THEN RETURN[NIL];
new ← LIST[entry.first];
end ← new;
FOR e: VoiceRopeDB.TuneList ← entry.rest, e.rest
WHILE e #
NIL
DO
end.rest ← LIST[e.first];
end ← end.rest;
ENDLOOP;
RETURN[new];
};