--- title: "Real Echo Example" id: 63227 type: "computer_media" slug: "real-echo-example" url: "http://localhost/computer_media/real-echo-example/" markdown_url: "http://localhost/computer_media/real-echo-example.md" published_at: "2026-01-31T13:57:53+00:00" modified_at: "2026-04-03T07:57:54+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/01/real-echo-example.png" excerpt: "A two-voice echo music player for that staggers the same melody across two SOUND channels to simulate a real acoustic echo effect." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Eric Boisvert" slug: "boisvert-eric" taxonomy: "indiv" url: "http://localhost/indiv/boisvert-eric/" - name: "Kristian Boisvert" slug: "boisvert-kristian" taxonomy: "indiv" url: "http://localhost/indiv/boisvert-kristian/" genre: - name: "Sound" slug: "sound" taxonomy: "genre" url: "http://localhost/type/sound/" media_contents: - id: 63047 title: "Byte Power December 1986 – January 1987" type: "computer_media" url: "http://localhost/computer_media/byte-power-december-1986-january-1987/" media_type: "Program" programmers: - name: "Eric Boisvert" slug: "boisvert-eric" taxonomy: "indiv" url: "http://localhost/indiv/boisvert-eric/" - name: "Kristian Boisvert" slug: "boisvert-kristian" taxonomy: "indiv" url: "http://localhost/indiv/boisvert-kristian/" mediadate: "1987" producer_company: - id: 25181 title: "Byte Power" type: "company" url: "http://localhost/company/byte-power/" images: - url: "http://localhost/wp-content/uploads/2026/01/real-echo-example.png" article_media: - id: 63091 title: "Better Programming!" type: "article" url: "http://localhost/article/better-programming-7/" media_type_tags: "Sound" --- # Real Echo Example This program plays a two-voice rendition of the Toccata by Leonardo Leo with a real-time echo effect using the SOUND command. Voice 1 carries the main melody loaded from a binary block at address 60003, while Voice 2 plays the same music but offset by a configurable delay stored at address 60000 (initially set to a PAUSE/254 rest). Each note is encoded as a three-byte triplet: pitch, second parameter, and duration, in a format described as MUSIcomp-compatible. The volume of the echo voice (channel 9) is intentionally set lower (12 vs. 15) than the main voice (channel 8) to simulate natural echo decay. The program saves both the BASIC loader and a 895-byte machine code music data block to tape, and reloads them together with a single RUN. *** ## Program Structure The program is organized into a short initialization phase, a main playback loop, two subroutines for advancing note pointers, and a tape save/load section at the end. 1. **Lines 6–10:** Initialize voice pointers `v1` (60003) and `v2` (60000), and poke a REST byte (254) and duration (1) at `v2` so the echo starts one note behind. 2. **Lines 15:** Prime both voices by calling subroutines 100 and 200 to load the first note of each voice into working variables. 3. **Lines 35–70:** Main loop — checks for end-of-music, sets volumes, plays via `SOUND`, pauses for tempo, decrements duration counters, and fetches the next note when a voice’s counter expires. 4. **Lines 100–120 / 200–220:** Subroutines that `PEEK` the three-byte note record (pitch, second parameter, duration) and advance the respective pointer by 3. 5. **Lines 9000 / 9999:** Tape utilities — `LOAD ""CODE 60003` loads the music data then `RUN`s the program; line 9999 saves both the BASIC and 895-byte code block. ## Note Data Format The music is stored as a flat array of 3-byte records starting at address 60003, described as MUSIcomp-compatible. Each record contains: | Offset | Variable | Meaning | | --- | --- | --- | | +0 | `a1` / `b1` | Pitch value passed to SOUND channel register 0 or 1 (or 254=rest, 255=end) | | +1 | `a2` / `b2` | Second SOUND parameter (e.g., octave/fine tune) passed to register 1 or 3 | | +2 | `a3` / `b3` | Duration in loop ticks (decremented each iteration; fetch next note when zero) | The 895-byte code block spans addresses 60003 to 60897, accommodating up to 298 three-byte note records for the main melody, preceded by a short echo-delay header at 60000–60002. ## Echo Mechanism The echo is achieved entirely in BASIC without a second melody data set. Voice 2’s pointer `v2` is initialized to 60000 — three bytes before the main melody at 60003. The bytes at 60000–60002 are pre-set by lines 10 to `254, ?, 1` (a one-tick rest), so Voice 2 starts one note-event behind Voice 1. As both pointers advance through the same music data at their own rates, Voice 2 always lags Voice 1, producing the echo. The REM at line 5 notes that the delay (pause) duration is controlled by `POKE V2+2,1`, making the lag adjustable. ## SOUND Command Usage Line 50 issues a single compound `SOUND` statement addressing seven AY registers in one call: - `8,a4` — Channel A amplitude (main melody volume, 0–15) - `9,b4` — Channel B amplitude (echo volume, fixed lower at 12 vs. 15) - `7,60` — Mixer register, value 60 (binary 00111100) enables tone on channels A and B, disables noise - `0,a1; 1,a2` — Channel A coarse and fine pitch - `2,b1; 3,b2` — Channel B coarse and fine pitch Lines 110 and 210 perform a brief volume-duck before each new note (`SOUND 8,14` / `SOUND 9,11`) to introduce a slight gap between consecutive notes of the same pitch, preventing them from blurring together. The REM explicitly notes this is kept non-zero for “smoother delivery.” ## Key BASIC Idioms and Techniques - Compound `LET` statements on a single line (e.g., line 105) minimise line overhead and slightly speed up execution. - Duration counters `a3`/`b3` are decremented in the main loop and subroutines are called only on expiry, avoiding unnecessary `PEEK` operations every tick. - The termination check at line 35 requires *both* voices to signal end-of-music (255) before stopping, preventing premature exit if the two voices have different lengths. - `PAUSE 4` at line 55 provides a fixed tempo tick; adjusting this value would change playback speed globally. ## Source Code ``` 1 REM REAL ECHO WRITTEN BY E & K BOISVERT ©1986 BYTE POWER TOCCATA BY Leonardo Leo 5 REM V1=ADDRESS OF MAIN MUSIC V2=ADDRESS OF ECHO AT FIRST V2 STARTS AT A PAUSE (DELAY OF ECHO) 6 LET v1=60003: LET v2=60000 9 REM POKE V2,254 FOR THE DELAY (PAUSE) POKE V2+2,1 DURATION OF DELAY 10 POKE V2,254: POKE V2+2,1 14 REM FIND FIRST NOTES OF VOICE 1 & 2 15 GO SUB 100: GO SUB 200 30 REM 255=END OF MUSIC 254=REST (PAUSE) (MUSIcomp COMPATIBLE DATA) 35 IF a1=255 AND B1=255 THEN STOP 39 REM A4 & B4 ARE THE VOLUME OF VOICE 1 & 2 40 LET a4=15: IF a1>=254 THEN LET a4=0 43 REM VOLUME OF VOICE 2 (ECHO) HAS TO BE LOWER TO CREATE EFFECT 45 LET b4=12: IF b1>=254 THEN LET b4=0 49 REM PLAY NOTES 50 SOUND 8,a4;9,b4;7,60;0,a1;1,a2;2,b1;3,b2 54 REM TEMPO (PAUSE 4) 55 PAUSE 4 59 REM A3 & B3 DURATION OF NOTE IF EQUAL 0 THEN FIND NEXT NOTE 60 LET a3=a3-1: IF a3=0 THEN GO SUB 100 65 LET b3=b3-1: IF b3=0 THEN GO SUB 200 69 REM COMPLETE LOOP 70 GO TO 35 100 REM FIND NOTE OF VOICE 1 AND ADD 3 TO V1 (NEXT NOTE OF VOICE 1) 105 LET a1=PEEK v1: LET a2=PEEK (v1+1): LET a3=PEEK (v1+2): LET v1=v1+3 109 REM LOWER VOLUME (IF NOT PAUSE OR END) TO SEPARATE NOTES (NOT 0 FOR SMOOTHER DELIVERY) 110 IF A1<254 THEN SOUND 8,14 120 RETURN 200 REM FIND NOTE OF VOICE 2 AND ADD 3 TO V2 (NEXT NOTE OF VOICE 2) 205 LET b1=PEEK v2: LET b2=PEEK (v2+1): LET b3=PEEK (v2+2): LET v2=v2+3 209 REM LOWER VOLUME (IF NOT PAUSE OR END) TO SEPARATE NOTES (NOT 0 FOR SMOOTHER DELIVERY) 210 IF B1<254 THEN SOUND 9,11 220 RETURN 8999 REM LOAD CODES FOR MUSIC 9000 LOAD ""CODE 60003: RUN 9999 SAVE "REAL ECHO" LINE 9000: SAVE "MUSIC"CODE 60003,895: VERIFY "REAL ECHO": VERIFY "MUSIC"CODE ```