Record:
PUBLIC
PROC[shhh: Thrush.
SHHH ← Thrush.none, credentials: Thrush.Credentials, serviceID: RefID.
ID, intID:
CARD ← 0, queueIt:
BOOL ←
TRUE]
RETURNS [nb: Thrush.
NB ← $success, voiceRope: VoiceRope] = {
Records a voice rope, registers it, and returns its ID. The following action reports are generated if intID#0:
{ $recording, $started, intID } when recording begins.
{ $recording, $finished, intID } when the recording of this interval has finished.
{ $recording, $abandoned, intID } when all recording and playback actions ending with this interval has been flushed.
ENABLE {
VoiceRopeDB.Error => {
VoiceUtils.Problem[explanation, $VoiceRope];
nb ← ec; GOTO Fail; };
Jukebox.Error, Jukebox.MissingChirp, Jukebox.
EOF => {
VoiceUtils.Problem["Error detected in Bluejay", $Bluejay];
nb ← $voiceStreamFailure; GOTO Fail; };
};
key: Thrush.EncryptionKey;
tune: Jukebox.Tune;
tuneID: Jukebox.TuneID;
intervalSpec: Jukebox.IntervalSpec ← NEW[Jukebox.IntervalSpecBody];
cDesc: BluejaySmarts.ConvDesc;
ir: BluejaySmarts.IntervalReq;
vrinfo: VoiceRopeDB.VoiceRopeInfo;
Check that conversation exists, etc.
[nb, cDesc] ← BluejaySmarts.GetConversation[smartsID: serviceID, conv: credentials.convID];
IF nb # $success THEN RETURN;
Lookup beep tune
IF beep =
NIL
THEN {
[voiceRope: beep] ← GetByInterest[shhh: shhh, class: "SysNoises", refID: "BeepTune"];
IF beep =
NIL
THEN
VoiceUtils.Problem["Can't find beep tune to prompt recording", $VoiceRope];
};
Get tune identifier
TRUSTED {
tune ← Jukebox.CreateTune[RecordingServiceRegister.jukebox, -1];
tuneID ← intervalSpec.tuneID ← tune.tuneId;
Jukebox.CloseTune[RecordingServiceRegister.jukebox, tune];
};
Have Bluejay record tune
note: playing the beep will flush if necessary
IF playBeep
AND beep#
NIL
THEN
[] ← Play[shhh: shhh, voiceRope: beep, credentials: credentials, serviceID: serviceID, queueIt: queueIt]
ELSE
IF
NOT queueIt
THEN
TRUSTED {
VoiceStream.FlushPieces[cDesc.info.stream]; -- Will report, synchronously
};
ir ←
NEW[BluejaySmarts.IntervalReqBody ← [
iSpec: intervalSpec^, direction: $record, cDesc: cDesc, requestingParty: credentials.partyID,
intID: intID, firstInterval: TRUE, lastInterval: TRUE]];
[nb] ← BluejaySmarts.SetInterval[ir];
IF nb # $success THEN RETURN;
intervalSpec.length ← ir.iSpec.length;
IF cDesc.keyTable #
NIL THEN
key ← cDesc.keyTable[1];
TRUSTED {
VoiceStream.AddPiece[cDesc.info.stream, intervalSpec, $record, ir];
};
Write voice rope entry in database
note: the length of the voice rope is unknown at this point since it has yet to be recorded
vrinfo.struct ← VoiceRopeDB.SimpleTuneList[tune: tuneID, start: 0, length: -1, key: key];
vrinfo.creator ← RPC.GetCaller[shhh];
vrinfo ← VoiceRopeDB.WriteVoiceRope[handle: RecordingServiceRegister.database, vr: vrinfo];
voiceRope ← NEW[VoiceRopeInterval ← [ropeID: vrinfo.vrID, start: 0, length: vrinfo.length]];
EXITS Fail => RETURN[nb, NIL];
};
Play:
PUBLIC
PROC[shhh: Thrush.
SHHH ← Thrush.none, voiceRope: VoiceRope, credentials: Thrush.Credentials, serviceID: RefID.
ID, intID:
CARD ← 0, queueIt:
BOOL ←
TRUE]
RETURNS [nb: Thrush.
NB ← $success] = {
Play a specified voice rope. If queueIt is FALSE, flush existing playback and recording requests, first. The following action reports are generated if intID#0:
{ $recording/$playback, $started, intID } as the interval begins playing/recording.
{ $recording/$playback, $scheduled, intID } as the interval is accepted for recording/playback, if there is already a list of pieces in the queue ahead of this request.
{ $recording/$playback, $finished, intID } when the interval has finished.
{ $recording/$playback, $abandoned, intID } when all recording and playback actions ending with this interval has been flushed.
ENABLE {
VoiceRopeDB.Error => {
VoiceUtils.Problem[explanation, $VoiceRope];
nb ← ec; GOTO Fail; };
Jukebox.Error, Jukebox.MissingChirp, Jukebox.
EOF => {
VoiceUtils.Problem["Error detected in Bluejay", $Bluejay];
nb ← $voiceStreamFailure; GOTO Fail; };
};
key: Thrush.EncryptionKey;
cDesc: BluejaySmarts.ConvDesc;
ir: BluejaySmarts.IntervalReq;
struct: VoiceRopeDB.TuneList;
intervalSpec: Jukebox.IntervalSpec;
playQueue, last: Jukebox.IntervalSpecs;
[nb, cDesc] ← BluejaySmarts.GetConversation[smartsID: serviceID, conv: credentials.convID];
IF nb # $success THEN RETURN;
IF
NOT queueIt
THEN
TRUSTED {
VoiceStream.FlushPieces[cDesc.info.stream]; -- Will report, synchronously
};
struct ← ReadVoiceRope[voiceRope].info.struct;
IF struct=NIL THEN { nb ← $noSuchVoiceRope; RETURN;};
distribute encryption keys for tune segments (and build play queue)
UNTIL struct=
NIL
DO
intervalSpec ← NEW[Jukebox.IntervalSpecBody];
[intervalSpec.tuneID, intervalSpec.start, intervalSpec.length, key, struct] ← VoiceRopeDB.NextTuneOnList[struct];
IF last =
NIL
THEN
last ← playQueue ← LIST[intervalSpec]
ELSE {
last.rest ← LIST[intervalSpec];
last ← last.rest;
};
[nb, intervalSpec.keyIndex] ← BluejaySmarts.DistributeKey[cDesc: cDesc, key: key, wait: struct=NIL];
SELECT nb FROM $success, $newKeys => NULL; ENDCASE => GOTO Fail;
ENDLOOP;
schedule the various tune segments for playback
FOR q: Jukebox.IntervalSpecs ← playQueue, q.rest
WHILE q#
NIL
DO
ir ←
NEW[BluejaySmarts.IntervalReqBody ← [
iSpec: q.first^, direction: $play, cDesc: cDesc, requestingParty: credentials.partyID,
intID: intID, firstInterval: q=playQueue, lastInterval: q.rest=NIL]];
[nb] ← BluejaySmarts.SetInterval[ir];
IF nb # $success THEN RETURN;
intervalSpec.length ← ir.iSpec.length;
TRUSTED {
VoiceStream.AddPiece[cDesc.info.stream, q.first, $play, ir];
};
ENDLOOP;
EXITS
Fail => RETURN[nb];
};
Stop:
PUBLIC
PROC[shhh: Thrush.
SHHH ← Thrush.none
, credentials: Thrush.Credentials, serviceID: RefID.
ID]
RETURNS [nb: Thrush.
NB ← $success] = {
Stop playing or recording some or all of the scheduled intervals.
Action reports are generated if intID#0 was specified in original request:
{ $recording, $abandoned, intID } as indicated above.
ENABLE {
Jukebox.Error, Jukebox.MissingChirp, Jukebox.
EOF => {
VoiceUtils.Problem["Error detected in Bluejay", $Bluejay];
nb ← $voiceStreamFailure; GOTO Fail; };
};
cDesc: BluejaySmarts.ConvDesc;
[nb, cDesc] ← BluejaySmarts.GetConversation[smartsID: serviceID, conv: credentials.convID];
IF nb # $success THEN RETURN;
TRUSTED {
VoiceStream.FlushPieces[cDesc.info.stream]; -- Will report, synchronously
};
EXITS Fail => RETURN[nb];
};