FILE: JukeboxIntervalImpl.mesa
Last Edited by: Stewart, December 30, 1983 11:38 am, Cedar5
Last Edited by: Swinehart, September 25, 1984 4:41:48 pm PDT
Ades, February 4, 1986 11:54:21 am PST
DIRECTORY
FS USING [Read],
Jukebox USING [ bytesPerChirp, FindChirp, Handle, MissingChirp, pagesPerChirp, RunType, Tune, WindowOrigin, singlePktLength, RunComponent, EnergyRange, hangoverPackets],
VM USING [AddressForPageNumber, Allocate, WordsForPages];
JukeboxIntervalImpl: MONITOR LOCKS self.LOCK USING self: Jukebox.Handle
IMPORTS FS, Jukebox, VM
EXPORTS Jukebox
SHARES Jukebox =
BEGIN OPEN Jukebox;
Tune: TYPE = Jukebox.Tune;
RunType: TYPE = Jukebox.RunType;
Exported procedure
FirstNoisyIntervalIn: PUBLIC PROC [
self: Handle, tune: Tune, start: INT, length: INT, minSilence: INT]
RETURNS [intStart: INT, intLength: INT𡤀, nextNoise: INT←-1] = {
variables referenced in the following nested procedures
ambientLevel: Jukebox.EnergyRange;
samplesSinceLastNonSilence: INT ← 0; -- any value will do, since we are not interested until the precedingSilence state, when we will reset it and rerun SetRunType anyhow
SetPosBefore: ENTRY PROC[self: Handle, tune: Tune, sample: INT]
RETURNS [intStart, intEnd: INT, runType: RunType] = {
ENABLE UNWIND => NULL;
Place "cursor" at the beginning of chirp containing sample.
IF tune.runData#NIL AND sample=tune.samplesToChirp+tune.samplesInChirp THEN {
intStart ← sample;
intEnd ← intStart + NextElementLength[tune];
runType ← tune.runType;
ambientLevel ← tune.runData.ambientLevel
}
ELSE
[intStart, intEnd, runType] ← SetPosToChirp[self, tune, sample/Jukebox.bytesPerChirp];
};
SetPosToChirp: INTERNAL PROC[self: Handle, tune: Tune, chirp: INT]
RETURNS [intStart, intEnd: INT, runType: RunType] = {
Place "cursor" within chirp containing sample, somewhere before run containing sample.
samplesStart: INT;
IF tune.runData=NIL THEN {
tune.runSpace ← VM.Allocate[count: 1];
tune.runData ← VM.AddressForPageNumber[tune.runSpace.page] +
((Jukebox.bytesPerChirp/2) - VM.WordsForPages[Jukebox.pagesPerChirp-1]);
tune.runChirp ← -1;
};
samplesStart ← MIN[chirp, tune.size]*Jukebox.bytesPerChirp;
IF chirp >= tune.size THEN {
tune.samplesToChirp ← tune.samplesInChirp ← -1;
RETURN[samplesStart, LAST[INT], end];
}; -- end of tune indication!
IF chirp#tune.runChirp THEN {
missing: BOOLFALSE;
chirpWindow: Jukebox.WindowOrigin;
tune.runChirp ← chirp;
chirpWindow ← Jukebox.FindChirp[self: self, tune: tune, chirp: chirp, signalMissingChirp: TRUE, signalEOF: TRUE!Jukebox.MissingChirp => {
missing←TRUE; CONTINUE; }];
IF ~missing THEN
{
FS.Read[file: chirpWindow.file, from: chirpWindow.base + ( Jukebox.pagesPerChirp - 1), nPages: 1, to: VM.AddressForPageNumber[tune.runSpace.page]];
read just the last page of each chirp
ambientLevel ← tune.runData.ambientLevel
}
ELSE -- empty chirp
tune.runData.runArray[0] ← [silence[Jukebox.bytesPerChirp]]
no need to set ambientLevel here, since it is only looked at when there are samples
};
tune.runIndex ← 0;
tune.samplesInChirp ← 0;
tune.samplesToChirp ← samplesStart;
SetRunType[tune];
RETURN[samplesStart, samplesStart + NextElementLength[tune], tune.runType];
};
NextRun: ENTRY PROC[self: Handle, tune: Tune]
RETURNS [intStart, intEnd: INT, runType: RunType] = {
Current "pointer" always denotes a valid entry.
Move to next valid entry, or indicate end.
ENABLE UNWIND => NULL;
tune.samplesInChirp ← tune.samplesInChirp + NextElementLength[tune];
IF tune.samplesInChirp=Jukebox.bytesPerChirp THEN {
[intStart, intEnd, runType] ← SetPosToChirp[self, tune, tune.runChirp+1];
RETURN;
};
tune.runIndex ← tune.runIndex+(IF tune.runData.runArray[tune.runIndex].elementType = soundEnergy THEN 2 ELSE 1);
SetRunType[tune];
runType ← tune.runType;
intStart ← tune.samplesToChirp+tune.samplesInChirp;
intEnd ← intStart + NextElementLength[tune];
};
NextElementLength: PROC[tune: Tune]
RETURNS [length: NAT ← 0]= {
SELECT tune.runData.runArray[tune.runIndex].elementType FROM
silence => length ← NARROW[tune.runData.runArray[tune.runIndex], Jukebox.RunComponent[silence]].length;
singlePkt => length ← singlePktLength;
soundEnergy => length ← NARROW[tune.runData.runArray[tune.runIndex+1], Jukebox.RunComponent[soundLength]].length;
soundLength => ERROR;
ENDCASE
};
SetRunType: PROC [tune: Tune] = {
procedure uses the ambient level, current energy level and time since last over-threshold sound to determine if we should regard next element as sound or silence: it is not called in end-of-tune cases
belowThreshold: BOOLEANTRUE;
samplesOmitted: BOOLEANFALSE;
WITH curr: tune.runData.runArray[tune.runIndex] SELECT FROM
silence => samplesOmitted ← TRUE;
singlePkt => belowThreshold ← (curr.energy <= ambientLevel);
soundEnergy => belowThreshold ← (curr.energy <= ambientLevel);
ENDCASE => ERROR;
IF ~belowThreshold THEN samplesSinceLastNonSilence ← 0;
tune.runType ← IF (~samplesOmitted) AND samplesSinceLastNonSilence <= Jukebox.hangoverPackets*Jukebox.singlePktLength THEN noisy ELSE silent;
IF belowThreshold THEN samplesSinceLastNonSilence ← samplesSinceLastNonSilence + NextElementLength[tune]
};
start of FirstNoisyIntervalIn routine body
searchState: {beforeStart, precedingSilence, inNoise, lessThanMinSilence, followingSilence} ← beforeStart;
runStart, runEnd: INT;
runType: RunType;
searchEnd: INT = start+length;
intStart←start;
IF start<0 OR length<0 THEN RETURN;
better to change the callers' parameter types, except lots of thing might need rewriting?
all 'nosuch found' returns have inStart=start and intLength=0
[runStart, runEnd, runType] ← SetPosBefore[self, tune, start];
IF runType = end THEN RETURN;
DO -- this is simply a state machine
SELECT searchState FROM
beforeStart =>
IF runEnd<start THEN [runStart, runEnd, runType] ← NextRun[self, tune] ELSE
{ searchState ← precedingSilence;
samplesSinceLastNonSilence ← Jukebox.hangoverPackets*Jukebox.singlePktLength;
resetting this and then calling SetRunType ensures that we don't pick up a short burst of below-ambience samples at the beginning of the NoisyInterval
SetRunType[tune];
runType ← tune.runType
};
precedingSilence =>
SELECT runType FROM
silent =>
IF runEnd >= searchEnd THEN {intStart←start; searchState ← followingSilence} ELSE
[runStart, runEnd, runType] ← NextRun[self, tune];
end =>
RETURN;
noisy =>
{intStart ← IF runStart > start THEN runStart ELSE start;
searchState ← inNoise};
ENDCASE => ERROR;
inNoise =>
SELECT runType FROM
noisy =>
IF searchEnd < runEnd
THEN {  intLength ← searchEnd - intStart;
   nextNoise ← searchEnd;
   RETURN
  }
ELSE [runStart, runEnd, runType] ← NextRun[self, tune];
end =>
{ intLength ← runStart - intStart; RETURN };
silent =>
{ intLength ← runStart - intStart; searchState ← lessThanMinSilence };
ENDCASE => ERROR;
lessThanMinSilence =>
SELECT runType FROM
silent =>
IF runEnd - (intStart + intLength) >= minSilence THEN searchState ← followingSilence
ELSE [runStart, runEnd, runType] ← NextRun[self, tune];
end =>
RETURN;
noisy =>
searchState ← inNoise;
ENDCASE => ERROR;
followingSilence =>
SELECT runType FROM
silent =>
[runStart, runEnd, runType] ← NextRun[self, tune];
end =>
RETURN;
noisy =>
{nextNoise ← runStart; RETURN};
ENDCASE => ERROR;
ENDCASE => ERROR;
ENDLOOP
};
END.