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; VoiceRopeImpl: CEDAR PROGRAM -- Should this be a monitor? IMPORTS FinchSmarts, FS, Rope, UserProfile, VoiceUtils, VoiceRopeDB EXPORTS VoiceRope ~ BEGIN OPEN VoiceRope; ROPE: TYPE ~ Rope.ROPE; defaultHandle: Handle_NIL; Open: PUBLIC PROC[voiceRopeDBName: Rope.ROPE _ NIL, voiceRopeDBInstance: Rope.ROPE _ NIL, localName: Rope.ROPE _ NIL, Complain: PROC[complaint: Rope.ROPE]_NIL] RETURNS [handle: Handle] = { 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; IF Complain = NIL THEN Complain _ FinchProblem; IF voiceRopeDBName = NIL THEN voiceRopeDBName _ Rope.Cat["/", server, "//", VoiceUtils.RnameToRspec[server].simpleName, "/VoiceRopeDB"]; 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] = { 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] = { ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; }; intervalSpec: VoiceTemp.IntervalSpec; key: Thrush.EncryptionKey; nb: Thrush.NB; handle _ ValidateHandle[handle]; IF StartFinch[handle]#running THEN RETURN; [nb, intervalSpec, key, handle.convID] _ handle.procs.recordTune[convID: handle.convID, queueIt: TRUE]; IF nb#$success THEN RETURN; 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]]; }; Play: PUBLIC PROC[handle: Handle_NIL, voiceRope: VoiceRope, queueIt: BOOL_TRUE, failOK: BOOL_FALSE] = { 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] = { ENABLE UNWIND => NULL; handle _ ValidateHandle[handle]; IF StartFinch[handle, FALSE]#running THEN RETURN; handle.procs.stopTune[handle.convID]; }; Retain: PUBLIC PROC [ handle: Handle _ NIL, voiceRope: VoiceRope, refID: Rope.ROPE, refIDType: Rope.ROPE ] ~ { handle _ ValidateHandle[handle]; }; Forget: PUBLIC PROC [ handle: Handle _ NIL, refID: Rope.ROPE, refIDType: Rope.ROPE ] ~ { handle _ ValidateHandle[handle]; }; Cat: PUBLIC PROC [handle: Handle _ NIL, vr1, vr2, vr3, vr4, vr5: VoiceRope _ NIL] RETURNS [new: VoiceRope] ~ { 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] ~ { 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] ~ { 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] ~ { ENABLE VoiceRopeDB.Error => { handle.Complain[explanation]; CONTINUE; }; handle _ ValidateHandle[handle]; len _ VoiceRopeDB.Read[handle.vdbHandle, vr.ropeID].length; }; 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; }; 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] ~ { 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] ~ { 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]; }; END. ๖VoiceRopeImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Doug Terry, June 5, 1986 11:56:21 am PDT Swinehart, June 26, 1986 4:10:42 pm PDT Operations for manipulating recorded voice. This should probably be separated into different modules: PlayRecordImpl, VoiceRopeImpl, VoiceInterestImpl. For the moment I just want to get something on which a voice editing tool can be built. ... Doug Intervoice operations (record and play) The following routines were adapted from RecordPlayImpl.mesa 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. default parameters put instance in db name set up handle 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. Records a voice rope, registers it , and returns its ID. A NIL return value indicates that something went wrong. Nothing much can be done about it; just tell the user. Should eventually log for system administrator's benefit. I don't know how you decided to name and lookup SysNoises Play[handle: handle, refID: "BeepTune", refIDType: "SysNoises", failOK: TRUE]; 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. 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 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. Sender and Walnut Message viewer STOP buttons Voice Interests Voice interests and the garbage collection of voice ropes is not yet implemented. Existence of refID retains interest in voiceFileID until corresponding Forget. Creator is assumed to be logged-in user. Remove refID from database, eliminating its interest in any voiceFileID's Creator is assumed to be logged-in user. Editing operations Concatenates together the non-NIL voice ropes to produce a new voice rope. Creates a new voice rope that is a substring of an existing voice rope. Creates a new voice rope in which the given interval of the voice rope "vr" is replaced by the voice rope "with". 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. Information about voice ropes Fetch: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope, index: INT] RETURNS [VoiceSample]; Miscellaneous Concatenates the two lists together (destructive to the first). Copies one list to another. Doug Terry, March 18, 1986 10:16:33 am PST created. Swinehart, June 25, 1986 11:06:22 am PDT New Thrush, Finch changes to: DIRECTORY, Open, Record, Play, Stop, Substr, Replace, DescribeRope ส ~˜codešœ™Kšœ ฯmœ1™