BEGIN ARRAY scale, freq, just[0:25], fifths[1:12];
INTEGER ARRAY major, minor[1:6], title[1:100];
INTEGER note, base, interval, char;
REAL ARRAY minorerror, majorerror[0:12];
REAL ARRAY lminorerror, lmajorerror[0:12];
REAL ARRAY tminorerror, tmajorerror[0:12];
REAL scalecents, justcents, cfreq;
REAL minorsum, majorsum, minorbigsum, majorbigsum;
REAL lminorsum, lmajorsum, lminorbigsum, lmajorbigsum;
REAL purefifth, pythag, syntonic, logmult;
INTEGER xcount, starcount, goodcount;
INTEGER outdev;

PROCEDURE format(n); VALUE n; INTEGER n; ioc(49);

PRODECURE circle heading;
BEGIN	text(1,
" C-G   G-D   D-A   A-E   E-B   B-F#  F#-C#  C#-G#  G#-D#  D#-A#  A#-F  F-C");
skip(1);
END circle heading;

PROCEDURE circle echo;
BEGIN	INTEGER f, c;
	text(outdev,
"   C-G    G-D    D-A    A-E    E-B    B-F#   F#-C#  C#-G#  G#-D#  D#-A#  A#-F   F-C*N   ");
	ioc(0); note := 0;
	FOR f := 1 STEP 1 UNTIL 12 DO
	BEGIN	WHILE note <= &*S AND note # 13 DO note := chin(7);
		FOR c := 1 STEP 1 UNTIL 7 DO
		BEGIN	IF note > &*S THEN
			BEGIN	chout(outdev, note); note := chin(7);
			END
			ELSE chout( outdev, &*S );
		END;
	END;
	skip(outdev);
	note := 0;
	FOR f := 1 STEP 1 UNTIL 12 DO
	BEGIN	scalecents := abs( 3.0*freq[note] - 2.0*freq[note+7] );
		rwrite( outdev, scalecents*60, 6, 0);
		note := note + 7; IF note > 11 THEN note := note - 12;
	END;
	skip(outdev);
	note := 0;
	FOR f := 1 STEP 1 UNTIL 12 DO
	BEGIN	scalecents := abs( 3.0*freq[note] - 2.0*freq[note+7] );
		rwrite( outdev, scalecents, 6, 2);
		note := note + 7; IF note > 11 THEN note := note - 12;
	END;
	skip(outdev);
END circle echo;

PROCEDURE write note( note );
INTEGER note;
BEGIN	IF note > 11 THEN note := note - 12;
CASE note OF
0:	text(outdev, "C " );
1:	text(outdev, "C#" );
2:	text(outdev, "D " );
3:	text(outdev, "D#" );
4:	text(outdev, "E " );
5:	text(outdev, "F " );
6:	text(outdev, "F#" );
7:	text(outdev, "G " );
8:	text(outdev, "G#" );
9:	text(outdev, "A " );
10:	text(outdev, "A#" );
11:	text(outdev, "B " )
ELSE    text(outdev, "??" );
END write note;

PROCEDURE set just scale;
BEGIN	logmult := 1200.00/ln(2);
	just[0] := 0; just[12] := 1200.00; just[24] := 2400.00;
	just[1] := 111.73; just[2] := logmult*ln(1.125);
	just[3] := logmult*ln(1.2); just[4] := logmult*ln(1.25);
	just[5] := logmult*ln(4.0/3.0); just[6] := 590.22;
	just[7] := logmult*ln(1.5); just[8] := logmult*ln(1.6);
	just[9] := logmult*ln(5.0/3.0); just[10] := logmult*ln(16.0/9.0);
	just[11] := logmult*ln(1.875);

	purefifth := just[7];
	pythag := 12.00 * purefifth - 8400.00;
	syntonic := 4.00 * purefifth - 2400.00 - just[4];

	FOR note := 1 STEP 1 UNTIL 11 DO
		just[note+12] := just[note]+1200.00;

	major[1] := 2; major[2] := 4; major[3] := 5;
	major[4] := 7; major[5] := 9; major[6] := 11;

	minor[1] := 2; minor[2] := 3; minor[3] := 5;
	minor[4] := 7; minor[5] := 8; minor[6] := 11;
END set just scale;

PROCEDURE ay38912( octave ); VALUE octave; REAL octave;
BEGIN

text(outdev, "*N  ");
FOR note := 0 STEP 1 UNTIL 11 DO
	rwrite( outdev, entier(125000.00/(freq[note]*octave) + 0.5), 8, 0 );

text(outdev, "*N  ");
FOR note := 0 STEP 1 UNTIL 11 DO
	rwrite( outdev, (freq[note]*octave), 8, 2 );

text(outdev, "*N  ");
FOR note := 0 STEP 1 UNTIL 11 DO
BEGIN	interval := entier(125000.00/(freq[note]*octave) + 0.5);
	rwrite( outdev, 125000.00/interval, 8, 2 );
END;

text(outdev, "*N  ");
cfreq := freq[0]*octave;
FOR note := 0 STEP 1 UNTIL 11 DO
BEGIN	interval := entier(125000.00/(freq[note]*octave) + 0.5);
	scalecents := 125000.00/interval;
	rwrite( outdev, logmult*ln(scalecents/cfreq) - scale[note], 8, 2 );
END;
END ay38912;

PROCEDURE write scale error( error, lerror );
VALUE error, lerror; REAL error, lerror;
BEGIN	IF error > 90.00 THEN format( 512+&X )
	ELSE IF error > 60.00 THEN format( 512+&** )
	ELSE IF error < 40.00 THEN format( 512+&= )
	ELSE IF (error < 48.80) OR (error < 48.90 AND lerror < 33.20)
		THEN format( 512+&E );
	rwrite(outdev, error, 8, 2);
	format( 512+&*S );
END;

PROCEDURE scale from fifths;
BEGIN	scale[0] := scalecents := 0; interval := 0;
	FOR note := 1 STEP 1 UNTIL 11 DO
	BEGIN	interval := interval + 7;
		scalecents := scalecents + fifths[note];
		IF interval > 11 THEN
		BEGIN	interval := interval - 12;
			scalecents := scalecents - 1200.00;
		END;
		scale[interval] := scalecents;
	END;
	FOR note := 0 STEP 1 UNTIL 11 DO
	BEGIN	scale[note+12] := scale[note]+1200.00;
		scalecents := scale[note] - scale[9];
		freq[note] := 440.0 * exp(scalecents/logmult);
		freq[note+12] := 2.0 * freq[note];
	END;

END scale from fifths;

PROCEDURE readcents;
BEGIN
	text( 1, "*NType 11 cent offsets from C*N" );
	scale[0] := 0;
	FOR note := 1 STEP 1 UNTIL 11 DO
	BEGIN	scale[note] := read(7);
	END;
	FOR note := 1 STEP 1 UNTIL 12 DO
		fifths[note] := scale[note+6] - scale[note-1];
	FOR note := 0 STEP 1 UNTIL 11 DO
	BEGIN	scale[note+12] := scale[note]+1200.00;
		scalecents := scale[note] - scale[9];
		freq[note] := 440.0 * exp(scalecents/logmult);
		freq[note+12] := 2.0 * freq[note];
	END;
END readcents;

PROCEDURE readfifths;
BEGIN	text( 1, "*NCircle of 11 fifths in cents from C*N" );

	circle heading;
	scalecents := 0;
	FOR note := 1 STEP 1 UNTIL 11 DO
	BEGIN	fifths[note] := read(7);
		scalecents := scalecents + fifths[note];
	END;
	fifths[12] := 8400.00 - scalecents;
	scale from fifths;
END readfifths;

PROCEDURE readcommas;
BEGIN INTEGER sign, num, den;
	text(1, "*NCircle of 12 fifths in commas commas from C");
	text(1, "*N0= pure, or <+|-><a/b><P|S>, e.g. +1P, -1/4P, +1/2S*N");

	circle heading;
	scalecents := 0;
	FOR note := 1 STEP 1 UNTIL 11 DO
	BEGIN
	next fifth:
		char := chin(7);
		IF char = &0 THEN sign := 0
		ELSE IF char = &- THEN sign := -1
		ELSE IF char = &+ THEN sign := 1
		ELSE GOTO next fifth;

		IF sign = 0 THEN fifths[note] := purefifth
		ELSE
		BEGIN	num := den := 0;
			char := chin(7);
			WHILE char >= &0 AND char <= &9 DO
			BEGIN num := 10*num + char - &0; char := chin(7);
			END;
			IF char = &/ THEN
			BEGIN	char := chin(7);
				WHILE char >= &0 AND char <= &9 DO
				BEGIN	den := 10*den + char - &0;
					char := chin(7);
				END;
				IF den = 0 THEN den := 1;
			END
			ELSE den := 1;

			interval := char MASK 223;
			IF interval = &P THEN fifths[note] := pythag
			ELSE fifths[note] := syntonic;
			fifths[note] :=	purefifth+fifths[note]*sign*num/den;
		END;
		scalecents := scalecents + fifths[note];
	END;
	fifths[12] := 8400.00 - scalecents;
	scale from fifths;
END readcommas;

{ Main program start }

set just scale;

outdev := 1;

get streams:
ioc(0); ioc(1); ioc(15); ioc(2);
outdev := output;
IF outdev < 0 THEN GOTO get streams;

skip(outdev);
text(1,"Title: ");
ioc(0); ioc(1); base := 1;
title char: char := chin(7);
IF char >= &*S THEN BEGIN title[base] := char; base := base+1; GOTO title char END;
title[base] := 0;

get input type:
ioc(0); ioc(1);
text(1, "*NNotes (cents), Fifths (cents) or fifths Commas N/F/C ? ");
note := chin(7) MASK 223;
IF note = &N THEN readcents
ELSE IF note = &F THEN readfifths
ELSE IF note = &C THEN readcommas
ELSE GOTO get input type;

base := 1;
WHILE title[base] # 0 DO
BEGIN	chout( outdev, title[base] ); base := base+1;
END;

skip( outdev );
circle echo;

text(outdev, "*NAY3 8912, True frequency, generated frequency, error in cents*N");
FOR note := 0 STEP 1 UNTIL 11 DO
BEGIN	text(outdev, "       "); write note( note );
END;
ay38912( 0.25 );
skip( outdev );
ay38912( 0.5 );
skip( outdev );
ay38912( 1.0 );
skip( outdev );
ay38912( 2.0 );
skip( outdev );
ay38912( 4.0 );

text(outdev, "*NPure fifth "); rwrite(outdev, purefifth, 8, 3);
text(outdev," Pythagorean comma "); rwrite(outdev, pythag, 8, 3);
text(outdev," Syntonic comma "); rwrite(outdev, syntonic, 8, 3);

text(outdev,
"*N*NKey: E equal tempered, = just, ** error greater than 10 cents, X syntonic comma error");
text(outdev,"*N");
text(outdev,"*NJust scale (cents), this one (cents, Hz)*N");
FOR note := 0 STEP 1 UNTIL 11 DO
BEGIN	text(outdev, "       "); write note( note );
END;

text(outdev,"*N  ");
FOR note := 0 STEP 1 UNTIL 11 DO
	rwrite( outdev, just[note], 8, 2 );
text(outdev,"*N  ");
FOR note := 0 STEP 1 UNTIL 11 DO
	rwrite( outdev, scale[note], 8, 2 );
text(outdev,"*N  ");
FOR note := 0 STEP 1 UNTIL 11 DO
	rwrite( outdev, freq[note], 8, 2 );

xcount := starcount := goodcount := 0;

text(outdev, "*NIntervals from each note*N" );
FOR note := 1 STEP 1 UNTIL 11 DO
BEGIN	text(outdev, "       "); write note( note );
END;

FOR note := 0 STEP 1 UNTIL 11 DO
BEGIN 	skip( outdev );	write note( note );
	FOR interval := 1 STEP 1 UNTIL 11 DO
	BEGIN	scalecents := scale[note+interval]-scale[note];
		justcents := just[interval];
		IF abs(scalecents-justcents) >= 21.49 THEN
			BEGIN	format( 512+&X );
				xcount := xcount + 1;
			END
		ELSE IF abs(scalecents-justcents) > 10.00 THEN
			BEGIN	format( 512+&** );
				starcount := starcount + 1;
			END
		ELSE IF abs(scalecents-justcents) < 1.00 THEN
			BEGIN	format( 512+&= );
				goodcount := goodcount + 1;
			END
		ELSE IF abs(scalecents-100.0*interval) < 1.00 THEN
			format( 512+&E );
		rwrite( outdev, scalecents, 8, 2 );
		format( 512+&*S);
	END;
END;

minorsum := majorsum := minorbigsum := majorbigsum := 0;
lminorsum := lmajorsum := lminorbigsum := lmajorbigsum := 0;

text(outdev, "*N*NAccumulated errors in each major and minor scale");
text(outdev,
"*NKey: = very good, E better than equal temperament, ** poor, X intolerable");
text(outdev, "*NEqual temp");
rwrite(outdev, 48.88, 8, 2); rwrite(outdev, 48.88, 8, 2);
text(outdev, "  3rd to 6th ");
rwrite(outdev, 33.24, 8, 2);
rwrite(outdev, 33.24, 8, 2);
text(outdev, " scale+internals ");
rwrite(outdev, 168.15, 8, 2); rwrite(outdev, 207.25, 8, 2);

FOR note := 0 STEP 1 UNTIL 11 DO
BEGIN	skip(outdev); write note( note );
	text(outdev, " scale  ");
	minorerror[note] := majorerror[note] := 0;
	lminorerror[note] := lmajorerror[note] := 0;
	FOR interval := 1 STEP 1 UNTIL 6 DO
	BEGIN
	scalecents := scale[note+minor[interval]] - scale[note];
	justcents := just[minor[interval]];
	minorerror[note] := minorerror[note] + abs(justcents - scalecents);
	IF interval > 1 AND interval < 6 THEN
		lminorerror[note] := lminorerror[note] +
			abs(justcents - scalecents);
	scalecents := scale[note+major[interval]] - scale[note];
	justcents := just[major[interval]];
	majorerror[note] := majorerror[note] + abs(justcents - scalecents);
	IF interval > 1 AND interval < 6 THEN
		lmajorerror[note] := lmajorerror[note] +
			abs(justcents - scalecents);
	END;

	minorsum := minorsum + minorerror[note];
	majorsum := majorsum + majorerror[note];
	write scale error( majorerror[note], lmajorerror[note] );
	write scale error( minorerror[note], lminorerror[note] );

	text(outdev, "  3rd to 6th ");
	lminorsum := lminorsum + lminorerror[note];
	lmajorsum := lmajorsum + lmajorerror[note];
	rwrite(outdev, lmajorerror[note], 8, 2);
	rwrite(outdev, lminorerror[note], 8, 2);

	text(outdev, " scale+internals ");
	tminorerror[note] := minorerror[note];
	tmajorerror[note] := majorerror[note];
	FOR base := 1 STEP 1 UNTIL 5 DO
	FOR interval := base+1 STEP 1 UNTIL 6 DO
	BEGIN
	scalecents := scale[note+minor[interval]] - scale[note+minor[base]];
	justcents := just[minor[interval]-minor[base]];
	tminorerror[note] := tminorerror[note] + abs(justcents - scalecents);
	scalecents := scale[note+major[interval]] - scale[note+major[base]];
	justcents := just[major[interval]-major[base]];
	tmajorerror[note] := tmajorerror[note] + abs(justcents - scalecents);
	END;

	minorbigsum := minorbigsum + tminorerror[note];
	majorbigsum := majorbigsum + tmajorerror[note];

	rwrite(outdev, tmajorerror[note], 8, 2);
	rwrite(outdev, tminorerror[note], 8, 2);
END;

text(outdev, "*N"); write(outdev, goodcount); text(outdev,"just intervals, ");
write(outdev, starcount); text(outdev,"errors > 10 cents, and ");
write(outdev, xcount); text(outdev,"syntonic comma errors");

text(outdev, "*NTotal errors. Equal temperament ");
rwrite(outdev, 586.58, 8, 2); rwrite(outdev, 586.54, 8, 2);
text(outdev,  " 3rd to 6th "); rwrite(outdev, 398.87, 8, 2);
rwrite(outdev, 398.83, 8, 2);
text(outdev, " with internal errors ");
rwrite(outdev, 2017.78, 8, 2); rwrite(outdev, 2487.04, 8, 2);
text(outdev, "*NTotal errors.  This temperament ");
rwrite(outdev, majorsum, 8, 2); rwrite(outdev, minorsum, 8, 2);
text(outdev, " 3rd to 6th ");
rwrite(outdev, lmajorsum, 8, 2); rwrite(outdev, lminorsum, 8, 2);
text(outdev, " with internal errors ");
rwrite(outdev, majorbigsum, 8, 2); rwrite(outdev, minorbigsum, 8, 2);

chout( outdev, &*P );

END FINISH
