This program is a music player that encodes and plays multiple songs using the TS2068’s SOUND command for polyphonic audio output. At startup, a subroutine at line 820 precomputes a two-dimensional frequency table stored in array f(80,2), calculating the high and low bytes of the period value for each MIDI-like note number using the formula based on 36.8 Hz and equal-temperament tuning (2^(1/12) per semitone). Song data is encoded in DATA statements using named constants (tw=2, fr=4, r9=49, f1=51, f4=54) alongside BASIC expressions like NOT PI (=0) and SGN PI (=1) as compact numeric literals. Each note is represented as a pitch/duration pair, with j=0 triggering a rest via SOUND with silence, j=99 marking the end of a song, and the graphics loop at line 80 drawing a rectangle on screen synchronized to each note. The program cycles through songs automatically, resetting when the screen coordinate counter exceeds 250.
Program Analysis
Program Structure
The program is organized into three logical sections:
- Initialization (lines 10–20): Sets display attributes, calls the setup subroutine, and initializes playback state variables.
- Playback loop (lines 30–110): READs note pairs from DATA, handles rests, end-of-song markers, screen wrapping, graphics, and sound output.
- Setup subroutine (lines 820–880): Precomputes a frequency lookup table and defines symbolic constants.
- Song data (lines 120–810): Encoded note/duration pairs for multiple songs, terminated by
99,0sentinels.
Frequency Table Precomputation
The subroutine at line 820 defines playback constants and builds a 2D array f(80,2) covering note numbers 30–80. For each note, the period in microseconds is computed as:
freq = 36.8 * (2^(1/12))^(f-6)
x = 1,750,000 / (16 * freq)
This period is then split into high byte (f(f,1)) and low byte (f(f,2)) for use with the two-register SOUND command. The base of 36.8 Hz and the offset of 6 semitones from note index are chosen to map the integer note numbers to standard equal-temperament pitches.
Symbolic Constants as DATA Tokens
Because BASIC DATA values must be numeric expressions, the program exploits several compact idioms to avoid spelling out repeated numbers:
| Symbol | Value | Meaning |
|---|---|---|
tw | 2 | Sixteenth note duration unit |
fr | 4 | Eighth note duration unit |
r9 | 49 | Note D (MIDI-like index) |
f1 | 51 | Note E |
f4 | 54 | Note F#/Gb |
NOT PI | 0 | Rest pitch / zero duration |
SGN PI | 1 | Shortest duration unit |
PI | 3.14159… | Used as ~3 duration value |
The use of NOT PI (which evaluates to 0) and SGN PI (which evaluates to 1) in DATA statements is a clever space-saving technique, as these are shorter token sequences than writing the digits directly in many cases, and they tokenize efficiently in Sinclair BASIC.
Playback Loop Logic
Each iteration of the loop at line 30 reads a pair j,k where j is pitch and k is duration:
j=99: End of song — pause briefly, clear screen, restart DATA from the top viaGO TO 20. BecauseGO TO 20does not issue aRESTORE, successive songs are simply delimited by99,0and the DATA pointer advances naturally.j=0: Rest — plays silence withSOUND 8,0and advances the time counteri.- Otherwise: Draws a rectangle at the current horizontal position using
PLOT/DRAW, plays the note viaSOUNDwith the precomputed frequency bytes fromf(j,…), then pauses fork*2frames.
Screen Coordinate Management
Variable i tracks horizontal position on screen; u tracks vertical position. Lines 60–70 implement a two-phase wrap using flag: when i reaches 250, if flag=0 the vertical position drops to 0 and flag is set; if flag=1 the screen is cleared and position resets to the top. This gives two rows of graphics before clearing, matching the two screen halves.
SOUND Command Usage
Line 90 uses a multi-channel SOUND invocation with channels 7, 8, 12, 11, and 13, configuring envelope and mixer settings for the AY-3-8912 sound chip. Line 100 then sets channels 0 and 1 to the note frequency bytes from the lookup table. This separates tone setup (line 90) from pitch assignment (line 100), enabling consistent envelope behavior across all notes.
Notable Techniques
- Precomputed floating-point frequency table avoids repeated transcendental calculations during playback.
- Integer split into 256-byte high/low registers mirrors the AY chip’s 12-bit tone period registers.
- Using
PIdirectly as a duration value (≈3.14) relies on BASIC’sPAUSEand arithmetic truncating or using the float value — effectively treating PI as approximately 3. - The
RESTOREis implicitly performed by jumping to line 20, which re-executesREADfrom the beginning of DATA — a standard Sinclair BASIC idiom for song looping. - Multiple songs are concatenated in DATA, each terminated by the
99,0sentinel, soGO TO 20at the end of one song simply starts reading the next song’s data without an explicitRESTORE.
Potential Anomalies
- At line 60,
LET u=iis executed afterLET i=0, souis always set to 0 rather than the intended prior value ofi. This may be intentional (dropping to the bottom of the screen) or a subtle ordering bug. PIused as a duration value produces a non-integer PAUSE argument; BASIC will truncate or round this, effectively treating it as 3, which may cause slight timing drift compared to integer durations.- The
DIM f(80,2)at line 830 uses variablefas both the array name and the loop variable in the immediately followingFOR f=30 TO 80. This works in Sinclair BASIC because arrays and simple variables occupy separate namespaces.
Content
Source Code
10 INK 0: PAPER 7: BORDER 7: CLS : GO SUB 820
20 LET u=100: LET i=0: LET flag=i
30 READ j,k
40 IF j=99 THEN PAUSE 100: CLS : GO TO 20
50 IF j=0 THEN SOUND 8,0: PAUSE 4*k: LET i=i+k: GO TO 30
60 IF i>=250 AND flag=0 THEN LET i=0: LET u=i: LET flag=1
70 IF i>=250 AND flag=1 THEN CLS : LET i=0: LET u=100: LET flag=i
80 PLOT i,u: DRAW 0,j: DRAW k,0: DRAW 0,-j: DRAW -k,0: LET i=i+k
90 SOUND 7,56;8,16;12,30;11,30;13,9
100 SOUND 0,f(j,2);1,f(j,1)
110 PAUSE k*2: GO TO 30
120 DATA f4,6,f4,6,52,PI,f1,PI,f1,6,50,PI,f1,PI,f1,16,NOT PI,tw,50,3
130 DATA f1,PI,f1,6,50,PI,f1,PI,f4,6,f1,PI,f4,PI,52,12,r9,6,NOT PI,PI,r9,PI,r9,6,48,PI,r9,3
140 DATA 49,6,48,PI,r9,3
150 DATA 52,16,NOT PI,tw,f1,PI,r9,PI,f1,PI,f4,9,56,9,56,PI,r9,16,NOT PI,tw,f4,6
160 DATA f4,6,52,PI,f1,PI,f1,6,50,PI,f1,PI,f1,16,NOT PI,tw,50,PI,f1,PI,f1,6,50,PI,f1,3
170 DATA 52,PI,f1,PI,r9,5,46,SGN PI,r9,12,47,6,NOT PI,PI,47,PI,47,6,46,PI,47,PI,50,6,r9,PI,47,3
180 DATA 59,15,NOT PI,PI,47,PI,r9,PI,f1,PI,f4,SGN PI,NOT PI,tw,47,PI,r9,PI,f1,PI,f4,SGN PI,NOT PI,tw,42,PI,44,5
190 DATA f1,SGN PI,r9,12,47,SGN PI,99,0
200 DATA 52,tw,57,tw,NOT PI,tw,52,tw,57,tw,NOT PI,tw,52,tw,57,6,NOT PI,fr,52,tw,57,tw,59,tw,57,tw,56,4
210 DATA 57,tw,59,tw,NOT PI,8,52,tw,56,tw,NOT PI,tw,52,tw,56,tw,NOT PI,tw,52,tw,56,6,NOT PI,fr,52,2
220 DATA 56,tw,57,tw,56,tw,f4,fr,56,tw,57,tw,NOT PI,6,64,tw,63,tw,64,tw,61,tw,60,tw,61,2
230 DATA 57,tw,56,tw,57,tw,52,tw,NOT PI,fr,r9,tw,50,tw,52,tw,f4,tw,56,tw,57,tw,59,tw,61,2
240 DATA 62,tw,59,tw,NOT PI,fr,62,tw,61,tw,62,tw,59,tw,58,tw,59,tw,56,tw,55,tw,56,tw,52,2
250 DATA 0,fr,64,tw,63,tw,64,tw,66,tw,64,tw,62,tw,61,tw,59,tw,57,tw,99,0
260 DATA 49,8,r9,8,f1,12,NOT PI,fr,r9,8,r9,8,f1,12,NOT PI,fr,r9,8,f1,8,52,8,f1,8
270 DATA 49,8,f1,fr,r9,fr,f4,16,44,8,40,8,44,8,45,8
280 DATA 44,8,44,fr,40,fr,39,16,r9,8,r9,8,f1,12,NOT PI,fr,r9,8,r9,8,f1,12,NOT PI,4
290 DATA 40,8,44,8,45,8,r9,8,f1,fr,r9,fr,45,8,44,16,99,0
300 DATA 37,SGN PI,NOT PI,tw,30,SGN PI,NOT PI,5,42,PI,42,PI,41,PI,39,PI,41,PI,NOT PI,PI,42,PI,44,PI,NOT PI,PI,37,SGN PI,NOT PI,2
310 DATA 30,SGN PI,NOT PI,5,42,PI,42,PI,41,PI,39,PI,41,PI,NOT PI,PI,42,PI,44,PI,NOT PI,PI,37,PI,42,PI,NOT PI,PI,45,3
320 DATA 49,6,47,PI,45,PI,NOT PI,PI,r9,PI,52,6,50,PI,r9,PI,NOT PI,PI,53,PI,56,6,f4,PI,53,PI,50,3
330 DATA 49,PI,47,PI,45,PI,44,PI,30,SGN PI,NOT PI,5,42,PI,42,PI,41,PI,39,PI,41,PI,NOT PI,PI,42,PI,44,3
340 DATA 0,PI,37,SGN PI,NOT PI,tw,30,SGN PI,NOT PI,5,42,PI,42,PI,41,PI,39,PI,41,PI,NOT PI,PI,42,PI,44,PI,0
350 DATA PI,37,PI,NOT PI,PI,r9,PI,52,6,50,PI,r9,PI,47,PI,45,PI,43,PI,47,PI,50,PI,42,3
360 DATA 41,PI,42,PI,44,PI,NOT PI,PI,45,SGN PI,NOT PI,tw,44,9,42,SGN PI,99,0
370 DATA 47,tw,NOT PI,tw,47,tw,r9,tw,NOT PI,tw,r9,tw,f1,tw,f4,tw,f1,tw,47,tw,NOT PI,tw,42,2
380 DATA 47,tw,NOT PI,tw,47,tw,r9,tw,NOT PI,tw,r9,tw,f1,6,47,tw,NOT PI,tw,42,tw,47,tw,NOT PI,tw,47,tw,r9,2
390 DATA 0,tw,r9,tw,f1,tw,f4,tw,f1,tw,47,tw,NOT PI,fr,56,tw,NOT PI,fr,r9,tw,NOT PI,tw,52,tw,f1,6,47,2
400 DATA 0,fr,59,tw,NOT PI,tw,59,tw,56,tw,NOT PI,tw,59,tw,58,tw,61,tw,58,tw,f4,tw,NOT PI,fr,59,tw,NOT PI,2
410 DATA 59,tw,56,tw,NOT PI,tw,59,tw,58,6,f4,tw,NOT PI,tw,f1,tw,52,tw,NOT PI,tw,f1,tw,52,tw,NOT PI,tw,f4,2
420 DATA 56,tw,NOT PI,tw,58,tw,59,tw,NOT PI,fr,56,tw,NOT PI,fr,r9,tw,NOT PI,tw,52,tw,f1,6,47,tw,99,0
430 DATA 47,PI,NOT PI,tw,r9,SGN PI,47,PI,NOT PI,tw,r9,SGN PI,f1,PI,NOT PI,tw,f4,SGN PI,56,PI,NOT PI,tw,f4,1
440 DATA 59,PI,NOT PI,tw,58,SGN PI,61,PI,NOT PI,tw,59,SGN PI,58,PI,NOT PI,tw,61,SGN PI,59,PI,NOT PI,tw,56,1
450 DATA f4,PI,NOT PI,tw,f4,SGN PI,56,PI,NOT PI,tw,f4,SGN PI,59,PI,NOT PI,tw,56,SGN PI,f4,PI,NOT PI,tw,f1,1
460 DATA 49,24,47,PI,NOT PI,tw,r9,SGN PI,47,PI,NOT PI,tw,r9,SGN PI,f1,PI,NOT PI,tw,f4,SGN PI,56,PI,NOT PI,tw,f4,1
470 DATA 56,PI,NOT PI,tw,58,SGN PI,61,PI,NOT PI,tw,59,SGN PI,58,PI,NOT PI,tw,61,SGN PI,59,PI,NOT PI,tw,56,1
480 DATA f4,PI,NOT PI,tw,f4,SGN PI,59,PI,NOT PI,tw,47,SGN PI,r9,6,f4,6,47,18,99,0
490 DATA 55,tw,f4,tw,f4,fr,55,tw,f4,tw,f4,fr,55,tw,f4,tw,f4,fr,62,fr,NOT PI,4
500 DATA 62,tw,61,tw,59,fr,59,tw,57,tw,55,fr,55,tw,f4,2
510 DATA 52,fr,52,fr,NOT PI,fr,f4,tw,52,tw,52,fr,f4,tw,52,tw,52,fr,f4,tw,52,2
520 DATA 52,fr,61,fr,NOT PI,fr,61,tw,59,tw,58,fr,58,tw,55,tw,f4,fr,f4,tw,52,2
530 DATA 50,fr,50,fr,NOT PI,fr,62,tw,61,tw,61,fr,64,fr,58,fr,61,4
540 DATA 59,fr,f4,fr,NOT PI,fr,62,tw,61,tw,61,fr,64,fr,58,fr,61,4
550 DATA 59,fr,62,fr,61,tw,59,tw,57,tw,55,tw,f4,fr,46,fr,47,fr,r9,4
560 DATA 50,fr,52,tw,50,tw,r9,fr,47,fr,f4,fr,NOT PI,fr,65,8
570 DATA 66,tw,NOT PI,6,65,8,66,tw,NOT PI,6,65,8
580 DATA 66,fr,65,fr,66,fr,65,fr,66,fr,99,0
590 DATA 50,PI,50,PI,52,PI,f4,PI,50,PI,f4,PI,52,PI,45,PI,50,PI,50,PI,52,PI,f4,PI,50,6
600 DATA 49,PI,NOT PI,PI,50,PI,50,PI,52,PI,f4,PI,55,PI,f4,PI,52,PI,50,PI,r9,PI,45,PI,47,PI,r9,PI,50,6
610 DATA 50,PI,NOT PI,PI,47,5,r9,SGN PI,47,PI,45,PI,47,PI,r9,PI,50,PI,NOT PI,PI,45,5,47,SGN PI,45,PI,43,PI,42,6
620 DATA 45,PI,NOT PI,PI,47,5,r9,SGN PI,47,PI,45,PI,47,PI,r9,PI,50,PI,47,fr,45,PI,50,PI,r9,PI,52,PI,50,6,50,6
630 DATA 99,0
640 DATA 42,SGN PI,NOT PI,SGN PI,42,SGN PI,NOT PI,SGN PI,42,SGN PI,NOT PI,SGN PI,47,SGN PI,NOT PI,5,51,SGN PI,NOT PI,PI,42,SGN PI,NOT PI,SGN PI,42,SGN PI,NOT PI,1
650 DATA 42,SGN PI,NOT PI,SGN PI,47,SGN PI,NOT PI,5,51,SGN PI,NOT PI,5,30,SGN PI,NOT PI,SGN PI,30,SGN PI,NOT PI,SGN PI,35,SGN PI,NOT PI,PI,47,SGN PI,NOT PI,1
660 DATA 47,SGN PI,NOT PI,SGN PI,46,SGN PI,NOT PI,SGN PI,46,SGN PI,NOT PI,SGN PI,44,SGN PI,NOT PI,SGN PI,44,SGN PI,NOT PI,SGN PI,42,8,NOT PI,tw,42,SGN PI,NOT PI,1
670 DATA 42,SGN PI,NOT PI,SGN PI,42,SGN PI,NOT PI,SGN PI,46,SGN PI,NOT PI,5,r9,SGN PI,NOT PI,PI,42,SGN PI,NOT PI,SGN PI,42,SGN PI,NOT PI,SGN PI,42,SGN PI,NOT PI,1
680 DATA 46,SGN PI,NOT PI,5,r9,SGN PI,NOT PI,5,37,SGN PI,NOT PI,SGN PI,37,SGN PI,NOT PI,SGN PI,30,SGN PI,NOT PI,PI,f4,tw,56,tw,f4,tw,52,2
690 DATA f1,tw,r9,tw,47,8,99,0
700 DATA 42,fr,42,fr,46,fr,r9,fr,r9,fr,NOT PI,fr,61,tw,NOT PI,tw,61,tw,NOT PI,6,58,tw,NOT PI,tw,58,tw,NOT PI,6,42,fr,42,4
710 DATA 46,fr,r9,fr,r9,fr,NOT PI,fr,61,tw,NOT PI,tw,61,tw,NOT PI,6,58,tw,NOT PI,tw,58,tw,NOT PI,tw,NOT PI,6,42,fr,42,4
720 DATA 46,fr,r9,fr,r9,fr,NOT PI,fr,61,tw,NOT PI,tw,61,tw,NOT PI,6,59,tw,NOT PI,tw,59,tw,NOT PI,6,41,fr,41,fr,44,fr,f1,fr,f1,4
730 DATA 0,fr,63,tw,NOT PI,tw,63,tw,NOT PI,6,59,tw,NOT PI,2
740 DATA 59,tw,NOT PI,6,41,fr,41,fr,44,fr,f1,fr,f1,fr,NOT PI,fr,63,tw,NOT PI,tw,63,tw,NOT PI,6,58,tw,NOT PI,tw,58,2
750 DATA 0,6,42,fr,42,fr,46,fr,r9,fr,f4,fr,NOT PI,fr,66,tw,NOT PI,tw,66,tw,NOT PI,6,61,tw,NOT PI,tw,61,tw,NOT PI,6,42,4
760 DATA 42,fr,46,fr,r9,fr,f4,fr,NOT PI,fr,66,tw,NOT PI,2
770 DATA 66,tw,NOT PI,6,63,tw,NOT PI,tw,63,tw,NOT PI,6,44,fr,44,fr,47,fr,f1,tw,NOT PI,tw,f1,14,NOT PI,tw,48,fr,r9,fr,58,16
780 DATA f4,fr,46,fr,46,8,44,fr,f1,8,r9,fr,42,fr,NOT PI,tw,42,tw,42,fr,NOT PI,8,r9,tw,NOT PI,tw,47,2
790 DATA NOT PI,6,r9,tw,NOT PI,tw,47,tw,NOT PI,6,r9,fr,58,16,56,fr,r9,tw,NOT PI,tw,46,tw,NOT PI,6,r9,tw,NOT PI,tw,46,tw,NOT PI,6,r9,4
800 DATA 56,16,f4,fr,r9,tw,NOT PI,tw,47,tw,NOT PI,6,r9,tw,NOT PI,tw,47,tw,NOT PI,6,r9,fr,58,16
810 DATA 56,fr,r9,fr,f4,fr,58,fr,61,8,59,fr,58,tw,58,tw,58,fr,56,tw,NOT PI,tw,f4,fr,NOT PI,8,99,0
820 LET tw=2: LET fr=4: LET r9=49: LET f1=51: LET f4=54
830 DIM f(80,2): FOR f=30 TO 80
840 PRINT AT PI,PI;"Setting f(";f;")"
850 LET freq=36.8*(2^(1/12))^(f-6)
860 LET x=1.75/(16*freq): LET x=x*1000000
870 LET f(f,1)=INT (x/256): LET f(f,2)=INT x-INT (x/256)*256
880 NEXT f: RETURN
890 SAVE "MusicBeep2" LINE 10
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
