TS-2068 Piano

Developer(s): Mike Rhees
Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Music

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 ERR so 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 e and f, 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 in c(i).
  • The key labels come from short DATA strings, decoded with VAL i$(1) and VAL i$(2) to place each black key and its letter.
  • Saving with LINE 8700 skips the table setup, so a reloaded recording is still in memory.

Potential Issues

  • Line 140 decays the second voice with f-SGN e instead of f-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.

Appears On

Music with pictures, printer utilities, drawing tools, games and home programs: over fifty programs from the SINCUS library tape that followed tape 103, rescued from disk.

Related Articles

Related Content

Image Gallery

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 0

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.