-- IntMusic.mesa -- edited by Woods, November 13, 1980 1:54 PM -- edited by Brotz, November 12, 1981 2:23 PM -- edited by Barth, December 15, 1980 3:46 PM DIRECTORY inD: FROM "InteractorDefs" USING [realTimeClock], Inline USING [LowHalf], ProcessDefs USING [DisableInterrupts, EnableInterrupts], SegmentDefs USING [MachineType, memConfig], TimeDefs USING [ReadClock]; IntMusic: PROGRAM IMPORTS Inline, ProcessDefs, SegmentDefs, TimeDefs EXPORTS inD = BEGIN machineType: SegmentDefs.MachineType = SegmentDefs.memConfig.AltoType; GetLongTime: PROCEDURE RETURNS [time: LONG CARDINAL] = INLINE BEGIN AltoITimeFormat: TYPE = MACHINE DEPENDENT RECORD [lowBits: [0 .. 1023], trailingZeros: [0 .. 63], highBits: CARDINAL]; AltoIITimeFormat: TYPE = MACHINE DEPENDENT RECORD [crud1: [0 .. 15], lowBits: [0 .. 1023], crud2: [0 .. 3], highBits: CARDINAL]; time _ TimeDefs.ReadClock[]; SELECT machineType FROM AltoI => NULL; AltoII, AltoIIXM => LOOPHOLE[time, AltoITimeFormat].lowBits _ LOOPHOLE[time, AltoIITimeFormat].lowBits; ENDCASE => time _ 0; LOOPHOLE[time, AltoITimeFormat].trailingZeros _ 0; END; -- of GetLongTime -- PlayNote: PROCEDURE [cps, ticks: CARDINAL] = BEGIN -- play given note for given time out: POINTER TO CARDINAL = LOOPHOLE[177016B]; nanosecs, startTime, rtinc: LONG CARDINAL; time, outState: CARDINAL; -- cannot read out^ directly, so keep private state of output port. IF cps = 0 THEN BEGIN time _ inD.realTimeClock^; UNTIL inD.realTimeClock^ - time >= ticks DO out^ _ 0 ENDLOOP; END ELSE BEGIN ProcessDefs.DisableInterrupts[]; nanosecs _ (1000000000+LONG[cps/2])/LONG[cps]; out^ _ outState _ 0; rtinc _ (nanosecs*LONG[32]) / LONG[38080]; time _ 2*Inline.LowHalf[(LONG[ticks]*38993920)/nanosecs]; startTime _ GetLongTime[]; THROUGH [0..time) DO out^ _ outState _ IF outState=0 THEN 177777B ELSE 0; ProcessDefs.EnableInterrupts[]; ProcessDefs.DisableInterrupts[]; UNTIL GetLongTime[] - startTime >= rtinc DO ENDLOOP; startTime _ startTime + rtinc; ENDLOOP; ProcessDefs.EnableInterrupts[]; END; END; -- of PlayNote -- -- PlayTune is the main procedure. It interprets its string as follows: A letter from "A" -- through "G" specifies a note. If the letter is followed by "#" then the corresponding -- sharp-note is played (meaningful only for C, D, F, G, and A). All notes are eighth- -- notes (five ticks, to be precise), but upper-case letters cause tones that are "held" the -- full time while lower-case notes last only three ticks followed by a brief rest. C is the -- bottom of the octave; B is higher than C. When ">" is encountered, all subsequent notes -- are an octave higher; a "<" lowers all subsequent notes by an octave. Going up more -- than 3 octaves is not permitted (the fourth ">" is ignored), and notes near the top of the -- highest octave may not be struck accurately. Finally, use "%" to get an eighth-rest. PlayTune: PUBLIC PROCEDURE [s: STRING] = -- Tweaks speaker output to play tune encoded in s. BEGIN secs: CARDINAL = 15; -- maximum number of seconds of music we can handle note: CARDINAL _ 0; -- number of notes represented by s cps, octaves, hold: ARRAY [1 .. 8*secs] OF CARDINAL; scale: TYPE = CHARACTER ['A .. 'G]; twelfths: ARRAY scale OF CARDINAL = [12, 14, 3, 5, 7, 8, 10]; card: CARDINAL; freq: LONG CARDINAL; octave: CARDINAL _ 8; root: LONG CARDINAL = 10595; -- 12th root of 2, times 10000 freqA: LONG CARDINAL = 1760; -- highest octave we'll bother with SELECT machineType FROM AltoI, AltoII, AltoIIXM => NULL; ENDCASE => RETURN; FOR card IN [0 .. s.length) DO SELECT s[card] FROM IN ['A..'G] => BEGIN cps[note _ MIN [note+1, 8*secs]] _ twelfths[s[card]]; octaves[note] _ octave; hold[note] _ 5; END; IN ['a..'g] => BEGIN cps[note _ MIN [note+1, 8*secs]] _ twelfths[s[card]+('A-'a)]; octaves[note] _ octave; hold[note] _ 3; END; '# => IF note>0 THEN cps[note] _ cps[note]+1; -- sharps are one twelfth-octave higher '< => octave _ MIN [octave*2, 256]; '> => octave _ MAX [octave/2, 1]; '% => BEGIN cps[note _ MIN [note+1, 8*secs]] _ 0; hold[note] _ octaves[note] _ 5; END; ENDCASE; ENDLOOP; FOR card IN [1..note] DO freq _ SELECT cps[card] FROM 0 => LONG[0], <12 => freqA * LONG[100], ENDCASE => freqA * LONG[200]; THROUGH [0..cps[card] MOD 12) DO freq _ freq*root/LONG[10000] ENDLOOP; cps[card] _ Inline.LowHalf[(freq/LONG[octaves[card]]+LONG[50])/LONG[100]]; IF card>1 AND cps[card]=cps[card-1] AND (hold[card-1] MOD 5)=0 THEN BEGIN hold[card] _ hold[card]+hold[card-1]; -- combine notes for smoother play hold[card-1] _ 0; END; ENDLOOP; FOR card IN [1..note] DO IF hold[card]#0 THEN PlayNote [cps[card], hold[card]]; IF hold[card] MOD 5#0 THEN PlayNote [0, 5-(hold[card] MOD 5)]; ENDLOOP; END; -- of PlayTune -- END. -- of IntMusic --z19932(635)\f1