Music Maker

This file is part of and Timex Sinclair Public Domain Library Tape 1005. Download the collection to get this file.
Developer(s): Bill Ferrebee
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Music

Music-Maker is a ZX81/TS1000 melody composition and playback program that lets the user define a tune measure by measure, specifying pitch and duration values for each note. Playback is driven by machine code accessed via USR 16514, with timing and pitch values written directly to memory locations 16516, 16518, 16520, and 16528 using POKE statements. The tempo is stored as a single-element array Z(1), calculated as 960 divided by the user-supplied tempo value, and passed to the machine code routine. Notes are stored in parallel arrays N() for pitch and D() for duration, with pitch value 100 used as a special rest code that triggers POKE 16528,255 instead of 254. After entry, the melody can be replayed, have its tempo adjusted, or be saved to tape under the user-supplied melody name.


Program Analysis

Program Structure

The program is divided into several functional sections linked by GOTO jumps rather than subroutines:

  1. Lines 20, 180–210: Title screen and initialisation — displays the “MUSIC-MAKER” and “MOUNTAINEER SOFTWARE 1984” banners, then clears the screen.
  2. Lines 220–350: Session setup — prompts for melody name (X$), number of measures (M), and tempo (T); computes Z(1)=960/T.
  3. Lines 360–670: Note entry — iterates over each measure (J) and each note within it (K), collecting pitch into N(I) and duration into D(I).
  4. Lines 30–170: Playback loop — executes the machine code routine for each note, then presents a post-play menu (replay, tempo change, or tape save).
  5. Lines 680–850: Tape save — counts down and issues SAVE X$ to record the program under the melody name.
  6. Lines 860–940: Tempo change — re-prompts for T and recomputes Z(1) before returning to the menu.
  7. Lines 950–960: Program self-save at line 950, followed by RUN.

Machine Code Sound Routine

All audio output is delegated to a machine code routine whose entry point is address 16514. Four memory locations are configured via POKE before each USR call:

AddressVariablePurpose
16516Z(1)Tempo/timing constant (960÷T), set once per play
16520N(I)Pitch value for current note
16518D(I)*1000/N(I)Duration scaled by pitch
16528254 or 255Rest flag: 255 = rest (N=100), 254 = normal note

The machine code is not defined within this BASIC listing; it is presumed to reside in memory already (likely POKEd in elsewhere or present in a companion loader). The result of USR 16514 is assigned to A at line 100 but the value is never used — this is simply the idiomatic way to call a machine code routine that returns a value.

Data Storage

Three arrays hold the session data:

  • Z(1) — single-element array holding the tempo constant passed to machine code.
  • N(X) — pitch values; pitch 100 is the special rest code.
  • D(X) — duration values; duration for note I is passed as D(I)*1000/N(I), scaling duration relative to pitch.
  • A(M) — note counts per measure; summed during entry to compute total note count X.

The total note count X is accumulated at line 410 inside the measure-count loop, and the N() and D() arrays are dimensioned only after all measure sizes are known (line 460–470), which is a correct two-pass approach.

Key BASIC Idioms

  • Busy-wait loops: FOR I=1 TO 50: NEXT I (and similar) are used as crude delays throughout, a common ZX81 technique in the absence of a PAUSE command in FAST mode.
  • FAST/SLOW switching: Playback runs inside FAST (line 30) to maximise the timing accuracy available to the machine code, with SLOW (line 120) restored for display output.
  • Centred text: AT 2,16-(INT (LEN X$/2));X$ at lines 380 and 510 centres the melody name by computing its half-length offset — a standard ZX81 centring idiom.
  • INKEY$ polling: Lines 140 and 700 use the IF INKEY$="" THEN GOTO spin-wait pattern to wait for any keypress.

Notable Techniques

The tempo formula Z(1)=960/T converts a musical tempo number into a machine-code-friendly period constant. The duration scaling D(I)*1000/N(I) means that for a given user duration value, higher-pitched (higher-frequency) notes have proportionally shorter machine code duration counts, which compensates for the relationship between pitch period and perceived note length.

The post-play menu (lines 130–170) is implemented without a PAUSE — it waits for a non-empty INKEY$ at line 140, then tests the same INKEY$ again at lines 150 and 160. Because INKEY$ is re-evaluated on each reference, there is a race condition: by the time lines 150 or 160 are reached, the key may have been released, causing the test to fail and falling through to line 170 (replay) even if S or T was pressed. A held key press is needed to reliably reach those branches.

Bugs and Anomalies

  • INKEY$ race (lines 140–160): As noted, each reference to INKEY$ is a fresh read; a brief keypress detected at line 140 may not still be held when tested at lines 150 and 160.
  • Missing line 630: The note-entry loop ends with NEXT J at line 620, but line numbers jump from 620 to 640 — line 630 is absent. This is harmless as it causes no execution gap.
  • Unused variable A: LET A=USR 16514 at line 100 stores the machine code return value in scalar A, which conflicts with the array A(M) dimensioned at line 360. On the ZX81, assigning to scalar A after DIM A(M) would erase the array. However, by the time playback reaches line 100, note entry is complete and A() is no longer needed, so no data is lost in practice.
  • Tape save countdown: The inner loop FOR J=1 TO 10: NEXT J in the countdown (lines 730–740, 770–780) provides very short delays between countdown digit updates — the countdown will appear nearly instantaneous rather than one second per digit.

Content

Appears On

Assembled by Tim Ward from many sources. Contains programs 10211 – 10251.

Related Products

Related Articles

Related Content

Image Gallery

Source Code

  10 REM NEXT A\##I-: INKEY$PEEK COPY ( UNPLOT INKEY$<= RETURN ( UNPLOT H4 LET 94 GOTO TAN 
  15 REM "MUSIC-MAKER"  1984         BILL FERREBEE                   749 HILL STREET NO. 6           PARKERSBURG, WV 26104           TO SAVE GOTO 950
  20 GOTO 180
  30 FAST 
  40 POKE 16516,Z(1)
  50 FOR I=1 TO X
  60 POKE 16520,N(I)
  70 POKE 16518,D(I)*1000/N(I)
  80 IF N(I)=100 THEN POKE 16528,255
  90 IF N(I)<>100 THEN POKE 16528,254
 100 LET A=USR 16514
 110 NEXT I
 120 SLOW 
 130 PRINT AT 7,4;"PRESS % %S%P%A%C%E%  TO STOP";AT 9,3;"PRESS % %T%  TO CHANGE TEMPO";AT 11,0;"PRESS % %S%  TO SAVE MELODY ON TAPE";AT 13,1;"PRESS ANY OTHER KEY TO REPLAY"
 140 IF INKEY$="" THEN GOTO 140
 150 IF INKEY$="S" THEN GOTO 680
 160 IF INKEY$="T" THEN GOTO 860
 170 GOTO 30
 180 PRINT AT 10,9;"% %M%U%S%I%C%-%M%A%K%E%R% ";AT 21,2;"% %M%O%U%N%T%A%I%N%E%E%R% %S%O%F%T%W%A%R%E% % %1%9%8%4% "
 190 FOR I=1 TO 100
 200 NEXT I
 210 CLS 
 220 DIM Z(1)
 230 LET X=0
 240 PRINT TAB 6;"% %M%U%S%I%C%-%M%A%K%E%R% %L%O%A%D%E%R% ";AT 3,0;"NAME OF MELODY ?: ";
 250 INPUT X$
 260 PRINT X$;AT 5,0;"HOW MANY MEASURES ?:";
 270 INPUT M
 280 PRINT M
 290 PRINT AT 7,0;"TEMPO ?:";
 300 INPUT T
 310 PRINT T
 320 LET Z(1)=960/T
 330 FOR I=1 TO 50
 340 NEXT I
 350 CLS 
 360 DIM A(M)
 370 FOR I=1 TO M
 380 PRINT AT 0,6;"% %M%U%S%I%C%-%M%A%K%E%R% %L%O%A%D%E%R% ";AT 2,16-(INT (LEN X$/2));X$;AT 4,0;"HOW MANY NOTES IN MEASURE ";I;" ?:";AT 4,30;"  "
 390 INPUT A(I)
 400 PRINT AT 4,30;A(I)
 410 LET X=X+A(I)
 420 FOR J=1 TO 50
 430 NEXT J
 440 NEXT I
 450 CLS 
 460 DIM N(X)
 470 DIM D(X)
 480 LET I=1
 490 FOR J=1 TO M
 500 FOR K=1 TO A(J)
 510 PRINT AT 0,16-(INT (LEN X$/2));X$
 520 PRINT AT 2,9;"   ";TAB 21;"   ";AT 2,0;"MEASURE :";J;TAB 15;"NOTE :";K
 530 PRINT AT 4,9;"    ";AT 6,12;"  "
 540 PRINT AT 4,0;"% %P%I%T%C%H%  :";
 550 INPUT N(I)
 560 PRINT N(I)
 570 PRINT AT 6,0;"% %D%U%R%A%T%I%O%N%  :";
 580 INPUT D(I)
 590 PRINT D(I)
 600 LET I=I+1
 610 NEXT K
 620 NEXT J
 640 FOR I=1 TO 50
 650 NEXT I
 660 CLS 
 670 GOTO 130
 680 CLS 
 690 PRINT AT 9,0;"PREPARE TAPE RECORDER FOR SAVING";AT 11,3;"(PRESS ANY KEY WHEN READY)"
 700 IF INKEY$="" THEN GOTO 700
 710 PRINT AT 9,0;"   START TAPE RECORDER NOW...   ";AT 11,3;"      COUNTDOWN : 5       "
 720 FOR I=4 TO 0 STEP -1
 730 FOR J=1 TO 10
 740 NEXT J
 750 PRINT AT 11,21;I
 760 NEXT I
 770 FOR J=1 TO 10
 780 NEXT J
 790 CLS 
 800 SAVE X$
 810 PRINT AT 10,5;"NAME OF SAVED PROGRAM:";AT 11,16-(INT (LEN X$/2));X$
 820 FOR I=1 TO 100
 830 NEXT I
 840 CLS 
 850 GOTO 130
 860 CLS 
 870 PRINT AT 1,0;"TEMPO ?:";
 880 INPUT T
 890 PRINT T
 900 LET Z(1)=960/T
 910 FOR I=1 TO 50
 920 NEXT I
 930 CLS 
 940 GOTO 130
 950 SAVE "1023%9"
 960 RUN 

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

Scroll to Top