<< SoundListImpl.mesa:>> <> <> <<>> 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 }; <<-------- public procedures --------->> 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 '!]]; <<15 represents 'all bits are sound' - i.e. 2**soundRopeCharDivisions-1>> 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.