DIRECTORY
Commander USING [CommandProc, Register],
IO USING [card, rope, PutF, PutRope, real, STREAM],
Rope USING [ROPE, Cat],
VoiceRope,
VoiceRopeServer,
VoiceRopeDB USING [Handle, --Header, Interest, InterestInfo,-- VoiceRopeInfo, Error, Open, EnumerateVoiceRopes, EnumProc, --EnumerateInterestClass, InterestProc, UnpackHeader, UnpackInterest,-- NextTuneOnList, TuneID, TuneList];
~
BEGIN
ROPE: TYPE ~ Rope.ROPE;
vrdb: ROPE ← "/Morley.lark//DCS/VoiceRopeServer/VoiceRopesLB";
vrdb: ROPE ← "/Strowger.lark//Strowger/VoiceRopesLB";
feedback: IO.STREAM; -- for feedback during statistics gathering
Usage statistics:
Storage
vrTotalSpace: CARD ← 0;
vfileTotalSpace: CARD ← 0;
Number of voice ropes that use a given voice file
maxVFID: CARD = 1000;
vrsByFile: ARRAY [0..maxVFID] OF CARDINAL ← ALL[0];
maxVrsPerTune: CARD = 25;
tunesByVrs: ARRAY [0..maxVrsPerTune] OF CARDINAL ← ALL[0];
Number of segments in a voice rope
maxVRSegs: CARD = 25;
vrsByNumSegs: ARRAY [0..maxVRSegs] OF CARD ← ALL[0];
Lengths of voice ropes
maxVRSize: CARD = 960000; -- two minutes of voice (8000*120)
vrBucketSize: CARD = 8000; -- one second buckets
vrsBySize: ARRAY [0..maxVRSize/vrBucketSize] OF CARD ← ALL[0];
vrsWithNegLength: CARD ← 0;
Average length
vrNum: CARD ← 0;
vrTotalLength: REAL ← 0; -- in samples
Segment sizes for voice ropes
maxSegSize: CARD = 480000; -- one minute of voice (8000*60)
bucketSize: CARD = 8000; -- one second buckets
segsBySize: ARRAY [0..maxSegSize/bucketSize] OF CARD ← ALL[0];
segsWithNegLength: CARD ← 0;
segsThatStartOnZero: CARD ← 0;
Average length
segsNum: CARD ← 0;
segsTotalLength: REAL ← 0; -- in samples
An actual voice rope
vr: VoiceRope.VoiceRope ← NEW[VoiceRope.VoiceRopeInterval];
PrintStats:
PUBLIC
PROC [out:
IO.
STREAM]
RETURNS [] ~ {
Prints the gathered stats.
numVrs, numTunes, numSegs: CARD ← 0;
IO.PutRope[out, "\n\n"];
IO.PutRope[out, "Voice ropes by number of tune segments:\n"];
FOR i:
CARD
IN [0..maxVRSegs]
DO
IO.PutF[out, " %g: %g\n", IO.card[i], IO.card[vrsByNumSegs[i]]];
numVrs ← numVrs + vrsByNumSegs[i];
ENDLOOP;
IO.PutF[out, "%g total voice ropes\n", IO.card[numVrs]];
IO.PutRope[out, "\n"];
IO.PutF[out, "Voice rope sizes (%g samples):\n", IO.card[vrBucketSize]];
FOR i:
CARD
IN [0..maxVRSize/vrBucketSize]
DO
IO.PutF[out, " <%g: %g\n", IO.card[i], IO.card[vrsBySize[i]]];
ENDLOOP;
IO.PutF[out, "Total # of voice ropes = %g\n", IO.card[vrNum]];
IO.PutF[out, "Total voice rope storage = %g\n", IO.real[vrTotalLength]];
IO.PutF[out, "Average voice rope length = %g\n", IO.real[vrTotalLength/vrNum]];
IO.PutF[out, "%g voice ropes have negative length\n", IO.card[vrsWithNegLength]];
IO.PutRope[out, "\n"];
IO.PutF[out, "Segment sizes (%g samples):\n", IO.card[bucketSize]];
FOR i:
CARD
IN [0..maxSegSize/bucketSize]
DO
IO.PutF[out, " <%g: %g\n", IO.card[i], IO.card[segsBySize[i]]];
numSegs ← numSegs + segsBySize[i];
ENDLOOP;
IO.PutF[out, "Total # of segments = %g\n", IO.card[segsNum]];
IO.PutF[out, "Total segment storage = %g\n", IO.real[segsTotalLength]];
IO.PutF[out, "Average segment length = %g\n", IO.real[segsTotalLength/segsNum]];
IO.PutF[out, "%g total segments\n", IO.card[numSegs]];
IO.PutF[out, "%g segments have negative length\n", IO.card[segsWithNegLength]];
IO.PutF[out, "%g segments start at zero\n", IO.card[segsThatStartOnZero]];
IO.PutRope[out, "\n"];
IO.PutRope[out, "Tunes by number of voice ropes:\n"];
FOR i:
CARD
IN [1..maxVFID]
DO
vrs: CARD = vrsByFile[i];
SELECT vrs
FROM
IN [1..maxVrsPerTune] => tunesByVrs[vrs] ← tunesByVrs[vrs] + 1;
> maxVrsPerTune => tunesByVrs[0] ← tunesByVrs[0] + 1;
ENDCASE => NULL;
ENDLOOP;
FOR i:
CARD
IN [0..maxVrsPerTune]
DO
IO.PutF[out, " %g: %g\n", IO.card[i], IO.card[tunesByVrs[i]]];
numTunes ← numTunes + tunesByVrs[i];
ENDLOOP;
IO.PutF[out, "%g total tunes\n", IO.card[numTunes]];
IO.PutRope[out, "\n"];
};
CollectStats: VoiceRopeDB.EnumProc ~ {
[info: VoiceRopeInfo] RETURNS [continue: BOOLEAN ← TRUE]
numSegs: CARD ← 0;
tid: VoiceRopeDB.TuneID;
tstart, tlen: INT;
tl: VoiceRopeDB.TuneList ← info.struct;
vrNum ← vrNum + 1;
WHILE tl#
NIL
DO
[tune: tid, start: tstart, length: tlen, rest: tl] ← VoiceRopeDB.NextTuneOnList[tl];
IF tid
IN
INT[1..maxVFID]
THEN vrsByFile[tid] ← vrsByFile[tid] + 1
ELSE vrsByFile[0] ← vrsByFile[0] + 1;
numSegs ← numSegs + 1;
IF
NOT (tl=
NIL
AND numSegs=1)
THEN {
-- only gather for multi-segment vrs
IF tlen>=0
THEN {
segsTotalLength ← segsTotalLength + tlen;
tlen ← tlen/bucketSize + 1;
};
SELECT tlen
FROM
<0 => segsWithNegLength ← segsWithNegLength + 1;
IN [1..maxSegSize/bucketSize] => segsBySize[tlen] ← segsBySize[tlen] + 1;
ENDCASE => segsBySize[0] ← segsBySize[0] + 1;
IF tstart = 0 THEN segsThatStartOnZero ← segsThatStartOnZero + 1;
};
segsNum ← segsNum + 1; -- total number of segments
ENDLOOP;
IF info.length < 0
THEN {
vr.ropeID ← info.vrID;
info.length ← VoiceRope.Length[vr: vr];
};
IF info.length>=0
THEN {
vrTotalLength ← vrTotalLength + info.length;
info.length ← info.length/vrBucketSize + 1;
};
SELECT info.length
FROM
<0 => vrsWithNegLength ← vrsWithNegLength + 1;
IN [1..maxVRSize/vrBucketSize] => vrsBySize[info.length] ← vrsBySize[info.length] + 1;
ENDCASE => vrsBySize[0] ← vrsBySize[0] + 1;
IF numSegs
IN [0..maxVRSegs]
THEN vrsByNumSegs[numSegs] ← vrsByNumSegs[numSegs] + 1
ELSE vrsByNumSegs[0] ← vrsByNumSegs[0] + 1;
IO.PutRope[feedback, " ."];
};
Analyze: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
Enumerates the voice rope database and gathers statistics.
ENABLE VoiceRopeDB.Error => {msg ← Rope.Cat["ERROR: ", explanation]; GOTO Error};
handle: VoiceRopeDB.Handle ← VoiceRopeDB.Open[dbName: vrdb];
IO.PutF[cmd.out, "Analyzing %g ...\n", IO.rope[vrdb]];
feedback ← cmd.out;
VoiceRopeDB.EnumerateVoiceRopes[handle: handle, start: NIL, proc: CollectStats];
PrintStats[cmd.out];
};
Commander.Register[key: "VRUsage", proc: Analyze, doc: "Gathers statistics about voice ropes by analyzing the database"];