DIRECTORY Rope USING [ROPE, Concat, FromChar], VoiceRope USING [IntervalSpecs], VoiceMarkers USING [DisplayCharMarks], VoiceViewers USING [VoiceViewerInfo, Sound, SoundList, SoundInterval, soundRopeCharDivisions, soundRopeResolution, soundRopeCharLength], SoundList; SoundListImpl: CEDAR PROGRAM IMPORTS Rope, VoiceMarkers EXPORTS SoundList = BEGIN AppendSound: PROC [list: VoiceViewers.SoundList, entry: VoiceViewers.Sound] RETURNS [VoiceViewers.SoundList] = { oneElementList: VoiceViewers.SoundList _ CONS[entry, NIL]; hangOffPoint: VoiceViewers.SoundList _ list; IF list = NIL THEN RETURN [oneElementList]; WHILE hangOffPoint.rest # NIL DO hangOffPoint _ hangOffPoint.rest ENDLOOP; hangOffPoint.rest _ oneElementList; RETURN [list] }; AppendSoundToSoundInterval: PROC [soundInterval: VoiceViewers.SoundInterval, entry: VoiceViewers.Sound] = { oneElementList: VoiceViewers.SoundList _ CONS[entry, NIL]; hangOffPoint: VoiceViewers.SoundList _ soundInterval.soundList; IF hangOffPoint = NIL THEN {soundInterval.soundList _ oneElementList; RETURN}; WHILE hangOffPoint.rest # NIL DO hangOffPoint _ hangOffPoint.rest ENDLOOP; hangOffPoint.rest _ oneElementList }; AppendSoundListToSoundList: PROC [head, tail: VoiceViewers.SoundList] = { hangOffPoint: VoiceViewers.SoundList _ head; IF tail = NIL THEN RETURN; WHILE hangOffPoint.rest # NIL DO hangOffPoint _ hangOffPoint.rest ENDLOOP; IF hangOffPoint.first.sound = 0 OR tail.first.silence = 0 THEN -- these two can be amalgamated { hangOffPoint.first.sound _ hangOffPoint.first.sound + tail.first.sound; hangOffPoint.first.silence _ hangOffPoint.first.silence + tail.first.silence; tail _ tail.rest }; hangOffPoint.rest _ tail }; SoundListFromIntervalSpecs: PUBLIC PROC [intervalSpecs: VoiceRope.IntervalSpecs, lengthOfRopeInterval: INT] RETURNS [soundList: VoiceViewers.SoundList _ NIL] = { lastSoundEnd: INT _ 0; FOR l: VoiceRope.IntervalSpecs _ intervalSpecs, l.rest WHILE l # NIL DO soundList _ AppendSound[soundList, [l.first.start - lastSoundEnd, l.first.length]]; lastSoundEnd _ l.first.start + l.first.length ENDLOOP; IF lengthOfRopeInterval > lastSoundEnd THEN soundList _ AppendSound[soundList, [lengthOfRopeInterval-lastSoundEnd, 0]] }; ExtractSoundList: PUBLIC PROC [voiceViewerInfo: VoiceViewers.VoiceViewerInfo, soundInterval: VoiceViewers.SoundInterval] = { soughtStart: INT _ soundInterval.ropeInterval.start; soughtEnd: INT _ soughtStart + soundInterval.ropeInterval.length; currStart, currEnd: INT _ 0; thisSilence, thisSound: INT; FOR l: VoiceViewers.SoundList _ voiceViewerInfo.soundList, l.rest WHILE l # NIL AND soughtEnd > currEnd DO currStart _ currEnd; thisSilence _ l.first.silence; thisSound _ l.first.sound; currEnd _ currStart + thisSilence + thisSound; IF soughtStart < currEnd THEN { IF soughtStart > currStart THEN { thisSound _ thisSound - MAX [soughtStart-currStart-thisSilence, 0]; thisSilence _ thisSilence - MIN [soughtStart-currStart, thisSilence] }; IF soughtEnd < currEnd THEN { thisSilence _ thisSilence - MAX [currEnd-soughtEnd-thisSound, 0]; thisSound _ thisSound - MIN [ currEnd-soughtEnd, thisSound] }; AppendSoundToSoundInterval[soundInterval, [thisSilence, thisSound]] } ENDLOOP }; ReplaceSoundList: PUBLIC PROC [voiceViewerInfo: VoiceViewers.VoiceViewerInfo, cutStart: INT, cutLength: INT, replacement: VoiceViewers.SoundList] = { tail, workingPtr: VoiceViewers.SoundList; endCurrSound, startCurrSound: INT _ 0; IF cutStart = 0 THEN { tail _ voiceViewerInfo.soundList; voiceViewerInfo.soundList _ NIL } ELSE { FOR workingPtr _ voiceViewerInfo.soundList, workingPtr.rest DO endCurrSound _ endCurrSound + workingPtr.first.silence + workingPtr.first.sound; IF endCurrSound >= cutStart THEN EXIT ENDLOOP; tail _ workingPtr.rest; workingPtr.rest _ NIL; IF endCurrSound > cutStart THEN -- have to 'move sound from workingPtr to tail' { silenceToMove: INT _ MAX [endCurrSound-cutStart-workingPtr.first.sound, 0]; soundToMove: INT _ MIN [endCurrSound-cutStart, workingPtr.first.sound]; tail _ CONS[[silenceToMove, soundToMove], tail]; workingPtr.first.silence _ workingPtr.first.silence - silenceToMove; workingPtr.first.sound _ workingPtr.first.sound - soundToMove } }; endCurrSound _ 0; DO startCurrSound _ endCurrSound; IF startCurrSound = cutLength THEN EXIT; endCurrSound _ endCurrSound + tail.first.silence + tail.first.sound; IF endCurrSound > cutLength THEN EXIT; tail _ tail.rest; ENDLOOP; IF startCurrSound < cutLength THEN -- delete some of the sound in the head of the list { silenceToSave: INT _ MAX [endCurrSound-cutLength-tail.first.sound, 0]; soundToSave: INT _ MIN [endCurrSound-cutLength, tail.first.sound]; tail.first.silence _ silenceToSave; tail.first.sound _ soundToSave }; IF replacement = NIL THEN replacement _ tail ELSE AppendSoundListToSoundList[replacement, tail]; IF voiceViewerInfo.soundList = NIL THEN voiceViewerInfo.soundList _ replacement ELSE AppendSoundListToSoundList[voiceViewerInfo.soundList, replacement] }; SoundChars: PUBLIC PROC [viewerInfo: VoiceViewers.VoiceViewerInfo, skipChars: INT _ 0] RETURNS [soundRope: Rope.ROPE _ NIL, remnant: INT] = { soundList: VoiceViewers.SoundList _ viewerInfo.soundList; partiallyFilled: BOOLEAN _ FALSE; partsFilled: [0..VoiceViewers.soundRopeCharDivisions] _ 0; soundMajority: ARRAY [0..VoiceViewers.soundRopeCharDivisions) OF BOOLEAN; samplesAccounted, soundComponents: [0..VoiceViewers.soundRopeResolution] _ 0; skipping: BOOLEAN _ skipChars>0; skipSamples: INT _ skipChars*VoiceViewers.soundRopeCharLength; AddChars: PROC [length: INT, sound: BOOLEAN] = { AddToPartChar: PROC [availableSamples: INT, sound: BOOLEAN] RETURNS [usedSamples: [0..VoiceViewers.soundRopeResolution]] = { partiallyFilled _ TRUE; usedSamples _ MIN[availableSamples, VoiceViewers.soundRopeResolution-samplesAccounted]; samplesAccounted _ samplesAccounted + usedSamples; IF sound THEN soundComponents _ soundComponents + usedSamples; IF samplesAccounted = VoiceViewers.soundRopeResolution THEN { soundMajority[partsFilled] _ soundComponents >= VoiceViewers.soundRopeResolution/2; samplesAccounted _ 0; soundComponents _ 0; partsFilled _ partsFilled + 1; IF partsFilled = VoiceViewers.soundRopeCharDivisions THEN { binaryOfChar: INT _ 0; partsFilled _ 0; partiallyFilled _ FALSE; FOR i: INT IN [0..VoiceViewers.soundRopeCharDivisions) DO binaryOfChar _ binaryOfChar*2 + (IF soundMajority[i] THEN 1 ELSE 0) ENDLOOP; soundRope _ soundRope.Concat[Rope.FromChar[IF binaryOfChar # 0 THEN ('A + binaryOfChar) ELSE '!]] -- 'A is the base type of the sound font [i.e. all segments clear] but the 'all clear' character is actually ! so as to make 'word selections' work in normal tioga terms } } }; WHILE partiallyFilled AND length>0 DO taken: INT _ AddToPartChar[length, sound]; length _ length - taken ENDLOOP; WHILE length >= VoiceViewers.soundRopeCharLength DO soundRope _ soundRope.Concat[Rope.FromChar[IF sound THEN ('A + 15) ELSE '!]]; length _ length - VoiceViewers.soundRopeCharLength ENDLOOP; WHILE length>0 DO taken: INT _ AddToPartChar[length, sound]; length _ length - taken ENDLOOP }; FOR l: VoiceViewers.SoundList _ soundList, l.rest WHILE l # NIL DO silence: INT _ l.first.silence; sound: INT _ l.first.sound; IF skipping THEN { IF silence >= skipSamples THEN { silence _ silence - skipSamples; skipping _ FALSE; AddChars[length: silence, sound: FALSE]; AddChars[length: sound, sound: TRUE] } ELSE { skipSamples _ skipSamples - silence; IF sound >= skipSamples THEN { sound _ sound - skipSamples; skipping _ FALSE; AddChars[length: sound, sound: TRUE] } ELSE skipSamples _ skipSamples - sound; } } ELSE { AddChars[length: silence, sound: FALSE]; AddChars[length: sound, sound: TRUE] } ENDLOOP; remnant _ IF partiallyFilled THEN partsFilled*VoiceViewers.soundRopeResolution+samplesAccounted ELSE 0; soundRope _ VoiceMarkers.DisplayCharMarks[soundRope, viewerInfo.charMarkList, skipChars] }; END. Î SoundListImpl.mesa: sound lists are descriptions of the silence/sound profiles of a ropeInterval and form part of the data structure behind a voice viewer Ades, April 22, 1986 2:15:13 pm PST these first few routines are the various append functions required by the public routines following them head is assumed non-nil -------- public procedures --------- voiceViewerInfo contains the sound list for a complete rope. Use the interval specification from soundInterval.ropeInterval to create a new sound list for that interval, placing it in soundInterval.soundList voiceViewerInfo contains the sound list for a complete rope. Cut out the specified portion and at the cutting point insert the replacement soundlist. no tests for shooting off the end in this routine: if we get an error then our caller has gone badly wrong and there is no obvious way to recover having got the head of the old rope in voiceViewerInfo.soundList, now delete the unwanted sound from the tail we can now join up our three lists: produce the graphical representation of the SoundList within a VoiceViewerInfo record. The caller already has the first skipChars of the representation in his hand, so omit these from the returned rope. If the sound list does not exactly fill a whole number of characters, return as remnant the number of samples left over. Any marker characters indicated in the VoiceViewerInfo are inserted 15 represents 'all bits are sound' - i.e. 2**soundRopeCharDivisions-1 ʘšœ™™†Jšœ$™$—J™—šÏk ˜ Jšœœœ˜$Jšœ œ˜ J˜&Jšœ œv˜ˆJšœ ˜ J˜—Jš Ïn œœœœœ ˜QJ˜J˜J™iJ™šž œœ;œ˜pJšœ)œœ˜:Jšœ,˜,Jšœœœœ˜+Jšœœœ"œ˜JJ˜#Jšœ˜ —J˜J˜šžœœK˜kJšœ)œœ˜:Jšœ?˜?Jšœœœ,œ˜OJšœœœ"œ˜JJ˜"—J˜J˜Jšžœœ)˜I™Jšœ,˜,Jšœ˜Jšœœœ"œ˜JJšœœœÏc˜^šœJ˜JJšœM˜MJ˜—J˜Jšœ˜—J˜J˜Jšœ$™$J˜š žœœœEœ&œ˜¡Jšœœ˜šœ4œœ˜GJšœS˜SJšœ-˜-—Jš˜Jšœv˜v—J˜J˜Jšžœœœ_˜|šœÏ™ÏJšœ œ$˜4Jšœ œ3˜AJšœœ˜Jšœœ˜J˜š œ?œœœ˜jJšœ˜Jšœ˜Jšœ˜Jšœ.˜.Jšœœ˜šœœ˜Jšœ˜šœœ(˜FJšœœ%˜D—J˜Jšœ˜Jšœ˜šœœ"˜DJšœœ ˜;—J˜JšœC˜C—J˜—Jš˜—J˜J˜š žœœœ;œ œ*˜•Jšœ—™—Jšœ)˜)Jšœœ˜&J˜Jšœ˜Jšœ˜šœ$˜$Jšœ˜—J˜Jšœ˜šœœ9˜Ašœ‘™‘JšœP˜PJšœœ˜%—Jšœ˜Jšœ˜Jšœœ˜J˜JšœœŸ/˜Ošœœœ3˜NJšœ œœ1˜GJšœœ%˜0JšœD˜DJšœ=˜=—J˜—J˜J˜Jšœm™mšœ˜Jšœ˜Jšœœœ˜(JšœD˜DJšœœœ˜&Jšœ˜—Jšœ˜J˜JšœœŸ4˜Wšœœœ.˜HJšœ œœ,˜BJšœ#˜#Jšœ˜—J˜J˜J™$Jšœœœœ/˜`Jšœœœ)œC˜——Jšœ˜J˜Jšž œœœ7œœœœ œ˜šœ‡™‡J˜9Jšœœœ˜!Jšœ:˜:Jšœœ*œœ˜IJšœM˜MJšœ œ˜ Jšœ œ.˜>J˜Jšžœœ œ œ˜0˜š ž œœœ œœ9˜|Jšœœ˜JšœœF˜WJ˜2Jšœœ1˜>Jšœ4˜6Jš˜šœV˜VJ˜J˜J˜Jšœ2˜4Jš˜Jšœœ˜˜J˜Jšœœ˜šœœœ*˜9Jšœ!œœœ˜C—Jšœ˜Jšœ+œœœŸ©˜‹—J˜—J˜—J˜šœœ ˜%Jšœœ ˜*Jšœ˜—Jšœ˜J˜šœ,˜3Jšœ+œœ œ˜MJšœF™FJšœ2˜2—Jšœ˜J˜šœ ˜Jšœœ ˜*Jšœ˜—Jš˜—J˜J˜šœ/œœ˜BJšœ œ˜Jšœœ˜Jšœ ˜ Jš˜šœœ˜Jš˜šœ#˜#Jšœ œ˜Jšœ!œ˜(Jšœœ˜$—J˜Jš˜šœ'˜'šœ˜Jš˜šœ˜Jšœ œ˜Jšœœ˜$—J˜Jšœ#˜'——J˜—J˜Jš˜šœ$œ˜+Jšœœ˜$—J˜—Jšœ˜Jšœ œœ?œ˜gJšœX˜X—J˜Jšœ˜J˜Jšœ˜—…—Ö,º