<> <> <> <> 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; <<>> <> FirstNoisyIntervalIn: PUBLIC PROC [ self: Handle, tune: Tune, start: INT, length: INT, minSilence: INT] RETURNS [intStart: INT, intLength: INT_0, nextNoise: INT_-1] = { <> 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; <> 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] = { <> 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: BOOL_FALSE; 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]]; <> ambientLevel _ tune.runData.ambientLevel } ELSE -- empty chirp tune.runData.runArray[0] _ [silence[Jukebox.bytesPerChirp]] <> }; 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] = { <> <> 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] = { <> belowThreshold: BOOLEAN _ TRUE; samplesOmitted: BOOLEAN _ FALSE; 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] }; <> 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; <> [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> 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.