Composition Editor

This file is part of and ISTUG Public Domain Library 6. Download the collection to get this file.
Developer(s): Bryan Lewis
Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Music

This program is a three-voice composition editor that allows users to enter, edit, and play back musical notes using the TS2068’s hardware sound chip (AY-3-8910). Notes are encoded as two-byte values stored in fixed-length string arrays, with each pair of characters holding the register values needed to set pitch on one of the chip’s three tone channels. Note names (c, d, e, f, g, a, b and their sharps) are pre-mapped to numeric indices via single-letter variable assignments at line 26, and a 3D array `n` translates note/octave pairs into the corresponding AY register byte values. Playback iterates through up to 400 note slots per voice, writing directly to AY registers via consecutive `SOUND` statements with a variable speed pause between steps. An edit mode allows real-time single-step navigation through a voice using key presses, with inline note replacement via subroutines at lines 1000 and 1050.


Program Analysis

Program Structure

The program is organized around a main menu at line 50 that dispatches to five sections via a computed GO TO at line 55: GO TO 80+opt*20. This maps menu options 1–5 to lines 100, 120, 140, 160, and 180 respectively. The five sections cover entering notes for Voice A, Voice B, Voice C, editing, and playback.

OptionTarget LineFunction
1100Enter Voice A notes
2120Enter Voice B notes
3140Enter Voice C notes
4160/200Edit a voice
5180Play back composition

Note Encoding and the `n` Array

A 3D numeric array n (implicitly dimensioned elsewhere or pre-populated) maps a note index and octave number to two AY-3-8910 register bytes. Line 16 seeds one entry: n(6,1,1)=228 and n(6,1,2)=4, which represents the coarse and fine pitch bytes for one note. At line 26, single-letter variables are assigned integer indices: c=1, cc=2, d=3, dd=4, e=5, f=6, ff=7, g=8, gg=9, a=10, aa=11, b=12. This allows the user to type note names like f1 or g2, from which n1 (note index, all characters except the last) and n2 (octave, last character) are extracted via VAL.

String Array Storage

Notes are stored in fixed-length two-character string arrays a$, b$, and c$, each dimensioned (1000,2) as noted in the REM at line 20. Each element pair holds the two AY register bytes as characters via CHR$, and they are retrieved during playback with CODE a$(j,1) and CODE a$(j,2). A rest is represented by storing CHR$ 0 in both elements.

Playback Mechanism

Playback at lines 190–194 uses consecutive SOUND statements to load AY registers directly. Registers 0–1 carry Voice A pitch, 2–3 carry Voice B pitch, and 4–5 carry Voice C pitch (with register 4 decremented by 1 at line 192, possibly to compensate for an off-by-one in the note table). Register 7 sets the mixer, and registers 8–10 set channel volumes. The loop runs to note 400 and restarts from the same start position, creating a loop. Pressing x during playback silences the chip and returns to the menu.

Edit Mode

Edit mode (lines 200–229) lets the user navigate through notes of a single voice in real time. A computed GO TO voice*10+200 at line 205 dispatches to the appropriate voice editor (210 for A, 220 for B). Within each editor, the current note plays continuously via SOUND, and INKEY$ polling at lines 216/226 moves forward ("7") or backward ("6") through note slots. Pressing "i" triggers a GO SUB to a note-input routine at line 1000 or 1050. There is no edit handler for Voice C — line 230 does not exist, so selecting voice 3 in edit mode would cause an error.

Key BASIC Idioms

  • Computed GO TO for menu dispatch: GO TO 80+opt*20 and GO TO voice*10+200.
  • Boolean arithmetic for navigation: LET j=j+(INKEY$="7")-(INKEY$="6") increments or decrements j based on key state.
  • INPUT ... LINE k$ used to accept unquoted string input including note names.
  • The last entered note is remembered in z$ and reused when the user presses ENTER without typing a new note (lines 107, 127, 147).
  • VAL n$ used to parse a numeric start position from string input.

Notable Techniques

The note string is parsed by splitting on length: k$(1 TO (LEN k$-1)) gives the note name/index portion, and k$(LEN k$) gives the octave digit. Since note indices are assigned to variables (f=6, etc.), the user can type something like f1 or g2, and VAL "f1"(1 TO 1) would return the value of variable f — a clever use of BASIC’s VAL evaluating variable names within strings.

The dummy FOR/NEXT loop at lines 22–24 (iterating 1 to 1000 with no body) is used as a progress indicator during a “Configuring memory now” message, serving as a visible delay while appearing to perform initialization work.

Bugs and Anomalies

  • Line 20 is a REM containing the DIM statements for a$, b$, and c$. These arrays are never actually dimensioned, so accessing them at runtime would produce an error unless the arrays were pre-existing in memory from a prior session.
  • Voice C has no edit subroutine — GO TO voice*10+200 with voice=3 targets line 230, which does not exist.
  • The typo “Compostion” (missing ‘i’) appears in the title at line 10, and “promt” (missing ‘p’) appears in the entry prompts at lines 100, 120, and 140.
  • Line 16 pre-populates one cell of array n before the array is dimensioned, which would cause an error on a fresh run; the array must be pre-loaded separately.
  • At line 192, CODE c$(j,1)-1 subtracts 1 from the Voice C coarse pitch register value, which may be intentional tuning compensation but is inconsistent with voices A and B.
  • Variable name case inconsistency: JJG and jjg are used interchangeably (lines 101–103, 203), which in Sinclair BASIC are treated as the same variable since variable names are case-insensitive for numeric variables.

Content

Appears On

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

Related Products

Related Articles

Related Content

Image Gallery

Composition Editor

Source Code

   10 BEEP .05,30: CLS : PRINT "Compostion Editor"'"V1.1 27 November 84"'"Bryan Lewis"
   16 LET n(6,1,1)=228: LET n(6,1,2)=4
   20 REM DIM a$(1000,2): DIM b$(1000,2): DIM c$(1000,2)
   21 CLS : PRINT "OK"''"Configuring memory now"
   22 FOR f=1 TO 1000
   24 NEXT f
   25 PRINT '''"OK"
   26 LET c=1: LET cc=2: LET d=3: LET dd=4: LET e=5: LET f=6: LET ff=7: LET g=8: LET gg=9: LET a=10: LET aa=11: LET b=12
   30 LET z$="stop"
   50 CLS : PRINT ;TAB 7;"Options";AT 5,0;TAB 7;"1. Enter Voice A"'TAB 7;"2. Enter Voice B"';TAB 7;"3. Enter Voice C"';TAB 7;"4. Edit";'TAB 7;"5. Play"
   51 SOUND 7,63
   52 INPUT opt
   54 CLS : IF opt>5 OR opt<1 THEN GO TO 50
   55 GO TO 80+opt*20
  100 PRINT "Enter note on promt. If no "'"change, press 'ENTER'"
  101 PRINT "Enter 'stop' to EXIT": PAUSE 600: INPUT "START NOTE#? ";n$: IF n$="" THEN LET JJG=1: GO TO 103
  102 LET jjg=VAL n$
  103 FOR j=JJG TO 1000
  104 PRINT AT 10,0;"NOTE#: ";j;"         "'''''"MEASURE: ";(j-1)/16+1;" in 4/4 (16th's)";AT 11,0;"NOTE: ";
  106 INPUT "NOTE? "; LINE k$: IF k$="r" THEN LET a$(j,1)=CHR$ 0: LET a$(j,2)=CHR$ 0: NEXT j
  107 IF k$="" THEN LET k$=z$
  108 IF k$="stop" THEN GO TO 50
  109 LET n1=VAL k$(1 TO (LEN k$-1))
  110 LET n2=VAL k$(LEN k$)
  111 LET z$=k$
  112 LET a$(j,1)=CHR$ n(n1,n2,1)
  113 LET a$(j,2)=CHR$ n(n1,n2,2)
  114 PRINT k$;" "
  115 NEXT j
  116 GO TO 50
  120 PRINT "Enter note on promt. If no "'"change, press 'ENTER'"
  121 PRINT "Enter 'stop' to EXIT": INPUT "START NOTE#? ";n$: IF n$="" THEN LET jjg=1: GO TO 123
  122 LET jjg=VAL n$
  123 FOR j=jjg TO 1000
  124 PRINT AT 10,0;"NOTE#: ";j;"     "
  125 PRINT ''''"MEASURE: ";(j-1)/16+1;" in 4/4 (16th's)";AT 11,0;"NOTE: ";
  126 INPUT "NOTE? "; LINE k$: IF k$="r" THEN LET b$(j,1)=CHR$ 0: LET b$(j,2)=CHR$ 0: NEXT j
  127 IF k$="" THEN LET k$=z$
  128 IF k$="stop" THEN GO TO 50
  129 LET n1=VAL k$(1 TO (LEN k$-1))
  130 LET n2=VAL k$(LEN k$)
  131 LET z$=k$
  132 LET b$(j,1)=CHR$ n(n1,n2,1)
  133 LET b$(j,2)=CHR$ n(n1,n2,2)
  134 PRINT k$;" "
  135 NEXT j
  136 GO TO 50
  140 PRINT "Enter note on promt. If no "'"change, press 'ENTER'"
  141 PRINT "Enter 'stop' to EXIT": INPUT "St NOTE#? ";n$: IF n$="" THEN LET jjg=1: GO TO 143
  142 LET jjg=VAL n$
  143 FOR j=jjg TO 1000
  144 PRINT AT 10,0;"NOTE#: ";j;"     "
  145 PRINT ''''"MEASURE: ";(j-1)/16+1;" in 4/4 (16th's)";AT 11,0;"NOTE: ";
  146 INPUT "NOTE? "; LINE m$: IF m$="r" THEN LET c$(j,1)=CHR$ 0: LET c$(j,2)=CHR$ 0: NEXT j
  147 IF m$="" THEN LET m$=z$
  148 IF m$="stop" THEN GO TO 50
  149 LET o1=VAL m$(1 TO (LEN m$-1))
  150 LET o2=VAL m$(LEN m$)
  151 LET z$=m$
  152 LET c$(j,1)=CHR$ n(o1,o2,1)
  153 LET c$(j,2)=CHR$ n(o1,o2,2)
  154 PRINT m$;" "
  155 NEXT j
  156 GO TO 50
  160 REM EDIT: GO TO 200
  161 GO TO 200
  180 CLS : PRINT "PLAY"
  181 INPUT "A Volume",avol: INPUT "B volume",bvol: INPUT "C Volume",cvol
  182 INPUT "Speed",sp,"START#? ";jjg
  185 SOUND 7,56;8,avol;9,bvol;10,cvol
  190 FOR j=jjg TO 400
  191 SOUND 0,CODE a$(j,1);1,CODE a$(j,2);2,CODE b$(j,1);3,CODE b$(j,2)
  192 SOUND 4,CODE c$(j,1)-1;5,CODE c$(j,2): PAUSE sp
  193 IF INKEY$="x" THEN SOUND 8,0;9,0;10,0: GO TO 50
  194 NEXT j: GO TO 190
  195 GO TO 50
  200 CLS : INPUT "What voice? (1=a, 2=b, 3=c) ";voice
  201 PRINT "Use ^ to go fwd"'"Use down arrow to go bckwd": PAUSE 480
  202 CLS 
  203 INPUT "START WHERE? (#) ";JJG
  205 GO TO voice*10+200
  210 LET j=JJG
  211 SOUND 0,CODE a$(j,1);1,CODE a$(j,2);7,62;8,15
  212 PRINT AT 10,0;"NOTE#: ";j;"    "
  214 IF INKEY$="x" THEN GO TO 50
  215 IF INKEY$="i" THEN GO SUB 1000
  216 LET j=j+(INKEY$="7")-(INKEY$="6"): IF j<1 THEN LET j=1
  219 GO TO 211
  220 LET j=JJG
  221 SOUND 0,CODE b$(j,1);1,CODE b$(j,2);7,62;8,15
  222 PRINT AT 10,0;"NOTE#: ";j;"    "
  224 IF INKEY$="x" THEN GO TO 50
  225 IF INKEY$="i" THEN GO SUB 1050
  226 LET j=j+(INKEY$="7")-(INKEY$="6"): IF j<1 THEN LET j=1
  229 GO TO 221
 1000 INPUT "NOTE? "; LINE m$: IF m$="r" THEN LET a$(j,1)=CHR$ 0: LET a$(j,2)=CHR$ 0: RETURN 
 1002 LET n1=VAL m$( TO (LEN m$-1)): LET n2=VAL m$(LEN m$): LET a$(j,1)=CHR$ n(n1,n2,1): LET a$(j,2)=CHR$ n(n1,n2,2)
 1004 RETURN 
 1050 INPUT "NOTE? "; LINE m$: IF m$="r" THEN LET b$(j,1)=CHR$ 0: LET b$(j,2)=CHR$ 0: RETURN 
 1052 LET n1=VAL m$( TO (LEN m$-1)): LET n2=VAL m$(LEN m$): LET b$(j,1)=CHR$ n(n1,n2,1): LET b$(j,2)=CHR$ n(n1,n2,2)
 1054 RETURN 
 9998 SAVE "janice" LINE 50

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

Scroll to Top