Music Beep 2

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

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:

  1. Initialization (lines 10–20): Sets display attributes, calls the setup subroutine, and initializes playback state variables.
  2. Playback loop (lines 30–110): READs note pairs from DATA, handles rests, end-of-song markers, screen wrapping, graphics, and sound output.
  3. Setup subroutine (lines 820–880): Precomputes a frequency lookup table and defines symbolic constants.
  4. Song data (lines 120–810): Encoded note/duration pairs for multiple songs, terminated by 99,0 sentinels.

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:

SymbolValueMeaning
tw2Sixteenth note duration unit
fr4Eighth note duration unit
r949Note D (MIDI-like index)
f151Note E
f454Note F#/Gb
NOT PI0Rest pitch / zero duration
SGN PI1Shortest duration unit
PI3.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 via GO TO 20. Because GO TO 20 does not issue a RESTORE, successive songs are simply delimited by 99,0 and the DATA pointer advances naturally.
  • j=0: Rest — plays silence with SOUND 8,0 and advances the time counter i.
  • Otherwise: Draws a rectangle at the current horizontal position using PLOT/DRAW, plays the note via SOUND with the precomputed frequency bytes from f(j,…), then pauses for k*2 frames.

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 PI directly as a duration value (≈3.14) relies on BASIC’s PAUSE and arithmetic truncating or using the float value — effectively treating PI as approximately 3.
  • The RESTORE is implicitly performed by jumping to line 20, which re-executes READ from the beginning of DATA — a standard Sinclair BASIC idiom for song looping.
  • Multiple songs are concatenated in DATA, each terminated by the 99,0 sentinel, so GO TO 20 at the end of one song simply starts reading the next song’s data without an explicit RESTORE.

Potential Anomalies

  • At line 60, LET u=i is executed after LET i=0, so u is always set to 0 rather than the intended prior value of i. This may be intentional (dropping to the bottom of the screen) or a subtle ordering bug.
  • PI used 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 variable f as both the array name and the loop variable in the immediately following FOR f=30 TO 80. This works in Sinclair BASIC because arrays and simple variables occupy separate namespaces.

Content

Appears On

Library tape of the Indiana Sinclair Timex User’s Group.

Related Products

Related Articles

Related Content

Image Gallery

Music Beep 2

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.

People

No people associated with this content.

Scroll to Top