Turns the computer keyboard into a two-octave piano played through the TS2068’s AY sound chip. The screen shows two rows of piano keys, each labelled with the computer key that plays it: the Q and number rows for the upper octave and the Z and A rows for the lower one. Up to three notes can sound at once, and each fades away after you release it. A mode menu offers Play, Record (up to 1,000 events), Playback at speed 1 to 4, and Save, which writes the program together with your recording to tape so it reloads ready to play back.
The REM in line 10 reads “BASICpiano – Mike Rhess TSUG-LV”, the Timex/Sinclair User Group of Las Vegas; the site’s spelling of his name is Mike Rhees, who wrote the “Piano” article in the CATS Newsletter of January 1988.
Program Analysis
Structure
- Lines 100–400: the play loop: call the keyboard scanner, set the AY tone and volume registers, and record events when in record mode.
- Lines 1000–1090: playback, timed by a machine code delay at 65425.
- Lines 6500–6550: save. Lines 7000–7110: the mode menu, with
ON ERRso BREAK returns to it. - Lines 8000–8130: set up the pitch tables
a()/b()from DATA and POKE 167 bytes of machine code to 65280–65446. - Lines 8700–8890: draw the keyboard and its key labels.
Machine Code
The 167 bytes are not documented in the listing. From the BASIC side, the routine at 65286 is called on every pass of the play loop and leaves up to three note numbers in 65280–65282 (0 for none). Playback calls 65442 once before starting, then calls 65425 and repeats the sound update while it returns a non-zero value, which paces the recorded events; the chosen speed is POKEd into 65433.
Notable Techniques
- Three AY channels are used: A and B for the first two notes with their own decaying volumes
eandf, and channel C for a third note using the envelope generator (SOUND 13,0). - Each recorded event stores the three note numbers as one three-character string in
a$(i)and the time inc(i). - The key labels come from short DATA strings, decoded with
VAL i$(1)andVAL i$(2)to place each black key and its letter. - Saving with
LINE 8700skips the table setup, so a reloaded recording is still in memory.
Potential Issues
- Line 140 decays the second voice with
f-SGN einstead off-SGN f(the playback version in line 1040 has it right), so in play mode that voice’s fade can stall or go negative. - Recording more than 1,000 events stops with a subscript error, which the error trap turns into a return to the menu.
This program came from an Oliger (JLO SAFE) disk and was converted to load from tape. Only the slash in its SAVE / had to be removed. It was tested in ZEsarUX.
Source Code
10 REM "BASICpiano" Mike Rhess TSUG-LV
20 GO TO 8000
100 LET a=USR 65286
110 LET b=PEEK 65280: LET c=PEEK 65281: LET d=PEEK 65282
120 IF NOT c THEN LET e=e-SGN e: GO TO 140
130 LET e=15: SOUND 1,a(c);0,b(c)
140 IF NOT d THEN LET f=f-SGN e: GO TO 160
150 LET f=15: SOUND 3,a(d);2,b(d)
160 IF b THEN SOUND 5,a(b);4,b(b);13,0
170 SOUND 8,e;9,f
200 IF r AND a THEN LET c(i)=a: LET a$(i)=CHR$ b+CHR$ c+CHR$ d: LET i=i+1
400 GO TO 100
1000 SOUND 7,56;10,16;12,25: RANDOMIZE USR 65442: FOR i=1 TO x: RANDOMIZE c(i+1)
1010 LET b=CODE a$(i,1): LET c=CODE a$(i,2): LET d=CODE a$(i,3)
1020 IF NOT c THEN LET e=e-SGN e: GO TO 1040
1030 LET e=15: SOUND 1,a(c);0,b(c)
1040 IF NOT d THEN LET f=f-SGN f: GO TO 1060
1050 LET f=15: SOUND 3,a(d);2,b(d)
1060 IF b THEN SOUND 5,a(b);4,b(b);13,0
1070 SOUND 8,e;9,f
1080 IF USR 65425 THEN GO TO 1020
1090 NEXT i: SOUND 8,0;9,0: PAUSE 60: GO TO 1000
6000 PRINT AT 2,0;"Press ""BREAK and SHIFT""","to Exit",,,,,,: RETURN
6500 REM tape
6510 GO SUB 6000: PRINT AT 3,8;"SAVE"
6520 INPUT "Enter SAVE name "; LINE i$: IF LEN i$>10 THEN GO TO 6520
6530 IF i$="" THEN LET i$="piano/data"
6540 PRINT AT 4,0;"SAVEing data under the name"'"""";i$;""""
6550 SAVE i$ LINE 8700: STOP
7000 SOUND 8,0;9,0: LET e=0: LET f=e: IF r THEN LET x=i-1: REM error trap
7010 IF INKEY$<>"" THEN GO TO 7010
7020 LET i=1: LET r=0: ON ERR RESET : PRINT AT 2,0;"Select Mode: 1=PLAY",,"2=RECORD",,"3=PLAYBACK",,"4=SAVE",
7030 LET i$=INKEY$: IF i$<"1" OR i$>"4" THEN GO TO 7030
7040 GO SUB 6000: ON ERR GO TO 7000: IF i$="1" THEN PRINT AT 3,8;"PLAY mode": GO TO 7100
7045 IF i$="4" THEN GO TO 6500
7050 IF i$="2" THEN LET r=1: PRINT AT 3,8;"RECORD mode": GO TO 7100
7055 IF INKEY$<>"" THEN GO TO 7055
7060 PRINT AT 3,8;"PLAYBACK mode"'"Speed"; FLASH 1;"?"; FLASH 0;" (1 thru 4)": LET i$=INKEY$: IF i$<"1" OR i$>"4" THEN GO TO 7060
7070 PRINT AT 4,0;" ": POKE 65433,VAL i$: GO TO 1000
7100 IF INKEY$<>"" THEN GO TO 7100
7110 GO TO 100
8000 REM setup tables
8010 CLEAR 65280: LET r=0: DIM a(40): DIM b(40)
8030 SOUND 7,56;10,16;12,25
8040 DIM a$(1000,3): DIM c(1000)
8100 RESTORE
8110 FOR a=1 TO 40: READ a(a),b(a): NEXT a
8130 RESTORE 8600: FOR a=65280 TO 65446: READ b: POKE a,b: NEXT a
8500 DATA 2,114,2,46,1,241,1,186,1,162,2,79,2,14,1,213,1,186,1,138
8510 DATA 0,221,0,209,0,186,0,165,0,156,0,234,0,221,0,197,0,175,0,165,0,98
8520 DATA 0,110,0,117,0,131,0,147,0,93,0,104,0,110,0,124,0,139,0,234
8530 DATA 1,7,1,39,1,75,1,95,0,248,1,23,1,57,1,75,1,116
8600 DATA 0,0,0,0,0,0,175,33,3,255,6,3,119,35,16,252,6,254,72,60,50,32,255,30,5,237
8610 DATA 80,203,10,56,33,62,0,33,0,255,197,6,3,190,40,69,35,16,250,33,3,255,175,6,3,190,40,5,35,16,250,24,18,58,32,255,119,193
8620 DATA 33,32,255,52,29,32,212,203,0,56,204,24,1,193,1,0,3,33,0,255,17,3,255,26,190,40,1,12,119,35,19,16,246,175,185,200,237
8630 DATA 75,120,92,50,120,92,50,121,92,201,35,35,35,175,190,40,197,229,33,3,255,175,6,3,190,40,6,35,16,250,225,24,199,227,126
8640 DATA 245,58,32,255,119,241,225,24,173,42,118,92,237,91,120,92,6,1,237,82,16,252,1,1,0,240,11,175,24,194,0
8650 DATA "14G","17K","034","067","10A","056","15H","001","11S","19 ","023","090","12D","18L","078"
8660 DATA "Qb f","WcZg","EdXa","ReCb","TfVc","YgBd","UaNe","IbMf","Oc g","Pd a"
8700 REM screen
8710 BORDER 4: PAPER 4: CLS : INK 9
8720 PRINT TAB 7; PAPER 2;" TS-2068 PIANO "
8730 LET i$=" ": FOR a=7 TO 13: PRINT PAPER 7;AT a,1;i$;AT a+8,0;i$: NEXT a
8740 FOR a=0 TO 250 STEP 24: PLOT a,0: DRAW 0,56: PLOT a+8,64: DRAW 0,56: NEXT a
8750 PLOT 0,0: DRAW 240,0: PLOT 8,64: DRAW 240,0
8760 RESTORE 8650: FOR a=0 TO 14: READ i$
8770 LET c=56+64*NOT VAL i$(1): LET b=4+24*VAL i$(2)+16*VAL i$(1): FOR b=b TO b+12: PLOT b,c: DRAW 0,-24: NEXT b
8780 PRINT AT 8+8*VAL i$(1),1+3*VAL i$(2)+2*VAL i$(1); PAPER 1;i$(3)
8790 NEXT a
8800 FOR a=1 TO 30 STEP 3: READ i$
8810 PRINT PAPER 6;AT 12,a+1;i$(1);AT 20,a;i$(3); PAPER 7;AT 10,a+1;i$(2);AT 18,a;i$(4)
8820 NEXT a
8830 PAPER 1
8890 GO TO 7000
9999 SAVE "BASICpiano" LINE 0Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
