Skip to content

Looking Back at a 1992 Amstrad CPC Music Encoder

Every now and then I dig through old discs, old listings, or old printouts and find a piece of code that takes me straight back to a very specific point in time…

This one is from 1992.

At the time I was asked to convert a commercial educational title called Money Matters from the BBC B to the Amstrad CPC, a program designed to help children learn about money and spending through four fun games. Somewhere along the way I needed a practical way to get music into the Amstrad CPC.

I had access to tools like EMU (Electronic Music Utility), which offered a visual sheet music editor. It was a little overkill for the needs of this project, and so I very quickly knocked up a BASIC program as a crude music compiler.

As is often the way, a “temporary” utility ended up becoming rather more interesting than first intended.

What I had was some sheet music of Fiddler on the Roof, which I had to arrange for the limitations of three channel sound and turn this into something the CPC could actually play. So I wrote a loader in Locomotive BASIC that would read note data from DATA statements, decode it, convert it into values suitable for the CPC sound chip, and store it into arrays for later playback.

That probably sounds simple enough on paper…

But looking back at the listing now, there are quite a few things in it that are rather charming, slightly clever, and very much of their time.

Want to see the source code?

it’s available on my github repo

https://github.com/muckypaws/AmstradCPC/blob/master/Commercial/Money%20Matters/SourceCode/Fiddler.bas

Want to hear how the music sounds?

It was really a tiny music compiler

The first thing that stands out is that this is not just a music player.

It is really a small compiler… albeit very crude/rough and ready, but it did the job to push the project forward.

Instead of trying to play notes directly from text strings such as G0,4 or A#-2,8, the program starts by reading the symbolic note data, decoding it, and storing the results into arrays for later use.

Later for the game, I would take those encoded note and store the compiled data to a file for reloading into the main game. This saved on memory hogging strings that would be better used for the games logic.

This was important because string handling in BASIC was not something you wanted to be doing in the middle of time-sensitive playback if you could avoid it. Parsing note names, working out octaves, handling rests, and converting note values into CPC sound periods would all take time. By doing that work up front, the playback stage could be much simpler and much more reliable.

So the program effectively does this in two phases…

  1. Read the score from DATA statements.
  2. Convert it into numeric pitch and duration values stored in arrays.

Only once that decoding stage is complete does the playback engine begin.

That separation is one of the nicest parts of the design, and probably explains why this old temporary program still feels quite structured when I look at it now.

The note data stayed readable to a human

One thing I still rather like is the format of the score data itself.

Notes are entered in a way that is readable to me as a person looking at sheet music:

DATA G0,4,F,4,G,4,F,4,E,8,C,8

That meant I could type note names, sharps, octave information and durations directly, rather than having to pre-convert everything into machine values by hand.

There is support for things like:

  • CD#A#
  • octave suffixes such as G0 or C1
  • lower octaves such as B-1
  • rests using R
  • section terminators using X

For a quick in-house utility, that is actually a very usable notation system.

In other words, I built something that let me work in musical terms first, and hardware terms afterwards.

But… But…. what about Flats Jason? Great question… For speed of parsing, and just getting this done, I took the easy route and converted everything to sharps instead. It’s not strictly in line with proper musical notation, and I’m fairly sure my old music teacher would have had something to say about it…

But it was quick, it was dirty, and it worked.

The DEF FN line is doing the heavy lifting

One of the most interesting lines in the program is this one:

DEF FNperiod%(note%)=ROUND(62500/(440*(2^(oct%+((note%-10)/12)))))

The CPC SOUND command does not accept note names. It needs a period value suitable for the AY sound chip. So rather than store a giant lookup table for every note in every octave, I used a formula.

That formula works from the familiar musical reference of A = 440Hz, then adjusts the frequency using semitone steps based on powers of two, and finally converts that into a CPC sound period.

So rather than hardcoding every possible pitch, the program calculates it.

The formula was published in the Amstrad CPC User Guide, thank fully we didn’t have to work that one out!

A packed note lookup string… because memory and code both mattered

Another nice little trick is this line:

tone$=" C C#D D#E F F#G G#A A#B R"

Later on, the code uses INSTR to locate the note inside that packed string and convert the note name into a numeric position.

It is a compact lookup table hidden inside a string.

4100 tone%=INSTR(tone$,note$)/2:note%=FNperiod%(tone%):RETURN

That avoided a long set of IF statements and kept the decoding logic relatively tidy. It is the sort of thing that made perfect sense when trying to keep a BASIC utility small and practical.

It also says quite a lot about the mentality of the time. You were always looking for neat little shortcuts… not necessarily because you had to, but because on 8-bit machines it quickly became second nature.

The parser had to deal with awkward note formats

The decoding routine itself is quite fun to revisit.

It reads a note token, checks whether it is a rest or terminator, reads the duration, and then peels apart the note text depending on how long it is.

That means it can cope with things like:

  • single-character notes
  • sharps
  • octave numbers
  • negative octaves

So CF#1B-1 and R can all be interpreted correctly.

4000 ' Decode Music Data
4010 READ note$:IF note$="X" THEN RETURN
4020 IF note$="R" THEN READ dur%:note%=0:RETURN
4030 READ dur%:IF LEN(note$)=1 THEN 4100
4040 ON LEN(note$)-1 GOSUB 4200,4300,4400
4100 tone%=INSTR(tone$,note$)/2:note%=FNperiod%(tone%):RETURN
4200 l$=RIGHT$(note$,1):IF l$="#" THEN RETURN ELSE oct%=VAL(l$):note$=LEFT$(note$,1)
4210 RETURN
4300 l$=RIGHT$(note$,2):IF LEFT$(l$,1)="#" THEN oct%=VAL(RIGHT$(l$,1)):note$=LEFT$(note$,2):RETURN
4310 note$=LEFT$(note$,1):oct%=VAL(RIGHT$(l$,2)):RETURN
4400 oct%=VAL(RIGHT$(note$,2)):note$=LEFT$(note$,2):RETURN

Again, for what was meant to be a temporary program, that is a fair amount of flexibility.

Really, what is happening here is that I had created a small music notation language for my own use and then written a parser for it in BASIC.

That is probably the most accurate way of describing it.

The score arrangement reused sections rather than duplicating everything

Another thing I noticed when reading back through the code is the use of RESTORE and pass variables to reuse chunks of score.

Rather than duplicate large amounts of DATA, where bars were repeated, the program jumps back to earlier sections or on to alternate sections depending on which pass it is on.

That is a very practical approach when entering music by hand.

If a phrase repeats, there is little point typing it all again if you can structure the program to revisit the existing data.

1050 ' Sort Out Melody Line Data
1052 PRINT"Coding CHANNEL A:"
1055 pass=1:pointer%=1
1060 RESTORE 10000:oct%=0
1080 GOSUB 4000:'Decode note data
1090 IF note$="X" THEN 1120
1100 left%(1,pointer%)=note%:left%(2,pointer%)=dur%:pointer%=pointer%+1
1110 GOTO 1080
1120 pass=pass+1:IF pass=2 THEN 1080
1130 IF pass=3 THEN 1060
1140 IF PASS=4 THEN RESTORE 10110:GOTO 1080
1150 fa=pointer%

So although the listing looks like a set of plain DATA statements at first glance, there is actually some arrangement logic going on behind the scenes.

It is not just playing notes. It is navigating sections of a score.

How the note timing actually worked

Each line of DATA statements represents a single bar of music.

Within that bar, the total duration adds up to 32 units, which became my internal timing system. From there, note lengths were defined as simple fractions of that bar:

  • 1  = 1/32 note
  • 2  = 1/16 note
  • 4  = 1/8 note
  • 8  = 1/4 note
  • 16 = 1/2 note
  • 32 = 1 bar

Yes, I know that a bar can be measured in 64th notes with the hemidemisemiquaver, so again my music teacher would be face palming as I didn’t follow proper convention.

So rather than thinking in terms of real-time durations, I was effectively working in relative musical time, where each bar was normalised to 32 units.

That made the score easy to reason about and, more importantly, easy to type in by hand from sheet music.

It also ties directly into the earlier mistake…

Because while the relative timing was nicely structured, the actual playback speed was controlled entirely by a hard-coded *3.5 multiplier in the SOUND command, more on that later…

Envelope shaping helped the notes sound less raw

The CPC’s sound hardware could be wonderfully expressive within its limits, but raw square-wave notes on their own could be a bit abrupt.

This line defines an envelope:

ENV 1,1,15,1,3,-1,1,5,0,1,12,-1,5

That envelope is then used by the playback routines so notes are not always just switched hard on and hard off.

It gave the tune more shape and made the result feel more musical.

Calling the arrangement “piano” in the comments may be a little generous given the hardware involved…

But it certainly helped.

The really interesting part… interrupt-driven playback in BASIC

This, for me, is the real star of the show.

Once the note data has been decoded into arrays, the program starts all three channels and then uses a Locomotive BASIC interrupt handler to keep the music going.

That is the clever part.

The key line is:

8040 EI:EVERY 1 GOSUB 8100

On the CPC, this means the BASIC interrupt routine is called regularly in the background. In this case, it checks the sound queue status for each channel and decides whether the next note needs to be fed into the hardware.

That means the foreground program can be doing something else entirely.

In this listing, I used INKEY$ in a loop and printed whatever key was pressed on screen. The point of that was not to build a user interface…

It was to prove that the music was still being serviced independently.

So while the program is sitting there waiting for keyboard input, the music continues.

You can type, see your keypresses appear, and hear that the playback does not stall.

That is such a lovely demonstration of how capable Locomotive BASIC actually was.

Using the sound queue properly

Rather than manually timing every single note in a giant blocking loop, the program checks the queue state using SQ() and only feeds another note when the relevant channel is ready.

That is a much smarter way to do it.

It lets the hardware manage note timing while the BASIC code acts as a scheduler.

In other words, the program is not trying to bit-bash music out in real time. It is topping up the queue as needed.

For an interpreted BASIC program, that is exactly the right kind of design.

On the first run through the music, I deliberately set the pointers for each channel to two:

8010 pta=2:ptb=2:ptc=2

Why?

Because lines 8020–8030 preload (or “prime”) each sound channel with the first note:

8020 SOUND &39,left%(1,1),left%(2,1)*3.5,0,1
8025 SOUND &3A,middle%(1,1),middle%(2,1)*3.5,0,0
8030 SOUND &3C,right%(1,1),right%(2,1)*3.5,0,1

This ensures all three channels are started in sync before the interrupt-driven routine takes over.

From that point on, the interrupt handler checks the queue status and feeds in subsequent notes only when required.

When the music reaches the end of all three channels, this line kicks in:

8123 SOUND &X111111,0,100,0,0:PTA=1:PTC=1:PTB=1

This effectively plays silence across all three channels, resets the pointers back to the start, and allows the music to loop cleanly… already in sync.

8000 ' Try All Channels Music Driver
8010 pta=2:ptb=2:ptc=2
8020 SOUND &39,left%(1,1),left%(2,1)*3.5,0,1
8025 SOUND &3A,middle%(1,1),middle%(2,1)*3.5,0,0
8030 SOUND &3C,right%(1,1),right%(2,1)*3.5,0,1
8040 EI:EVERY 1 GOSUB 8100
8050 a$=INKEY$:IF a$="" THEN 8050 ELSE PRINT a$;:GOTO 8050
8100 ON SQ(1) GOSUB 8200
8110 ON SQ(4) GOSUB 8300
8120 ON SQ(2) GOSUB 8400
8122 IF NOT(FC=PTC AND FA=PTA AND fb=ptb) THEN RETURN
8123 SOUND &X111111,0,100,0,0:PTA=1:PTC=1:PTB=1
8130 RETURN

The DI trick is very CPC BASIC

Inside the channel routines I disable interrupts briefly with DI before manipulating the queue pointers.

That prevented the handler from re-entering at an awkward moment while it was in the middle of servicing a channel.

One of the nice quirks of Locomotive BASIC was that, after returning from the subroutine, interrupt handling would effectively resume without needing lots of extra housekeeping in the code.

That made it a very convenient environment for this sort of experiment.

It is one of those details that might not mean much at first glance, but it is absolutely part of why this approach worked.

Rests were handled very simply

A rest is just stored as a note value of zero.

That sounds almost too obvious to mention, but it is actually a very clean design choice. The playback engine does not need a special path for silence. A rest is just another event with a duration, but no sounding pitch.

That means notes and rests are represented in the same stream of timed events.

Simple… tidy… effective.

Handling uneven channels… without wasting memory

There’s another small detail in the playback routines that turned out to be surprisingly important:

8200 DI:IF pta>=fa THEN RETURN
8300 DI:IF ptc>=fc THEN RETURN
8400 DI:IF ptb>=fb THEN RETURN

Each channel checks whether it has reached the end of its data before attempting to queue another note.

If it has… it simply returns and does nothing.

That might seem trivial, but it solves a couple of problems at once.

Firstly, it prevents the pointers from overrunning the arrays… no out-of-bounds reads, no garbage data being played, no unexpected behaviour.

More interestingly though, it means each channel can finish independently.

If the bass line ends a few bars before the melody, or the middle voice drops out early, that channel simply stops feeding new notes… while the others continue as normal.

Everything stays in sync, because the timing is still being driven by the interrupt and the sound queues… not by forcing all three channels to have identical lengths.

The alternative would have been to pad the shorter channels with rests just to keep everything aligned.

That would have worked… but it would have wasted memory and made the data more cumbersome to manage.

Instead, this approach lets each channel be as long (or as short) as it needs to be.

Which, for a hand-typed score on an 8-bit machine… felt like a win.

One mistake I should have spotted… the tempo was effectively hard-coded

Looking back at the code now, there is one fairly fundamental design mistake in how I handled note duration.

The note values in the score were based on musical duration… so smaller numbers represented shorter notes, and larger values such as 16, 24, and 32 represented longer ones. In other words, the score data itself was acting as a simple timing system built around note lengths.

That part was fine.

The mistake was what I did next.

When passing the duration into the CPC SOUND command, I multiplied every value by 3.5:

8020 SOUND &39,left%(1,1),left%(2,1)*3.5,0,1
8025 SOUND &3A,middle%(1,1),middle%(2,1)*3.5,0,0
8030 SOUND &3C,right%(1,1),right%(2,1)*3.5,0,1

and again in the playback routines:

8205 IF LEFT%(1,pta)<>0 THEN SOUND 1,LEFT%(1,pta),left%(2,pta)*3.5,0,1 ELSE SOUND 1,LEFT%(1,pta),left%(2,pta)*3.5,0,0
..
8310 IF right%(1,ptc)<>0 THEN SOUND 4,right%(1,ptc),right%(2,ptc)*3.5,0,1 ELSE SOUND 4,right%(1,ptc),right%(2,ptc)*3.5,0,0
..
9020 IF LEFT%(1,I)<>0 THEN SOUND 1,LEFT%(1,I),left%(2,i)*3.5,0,1 ELSE SOUND 1,LEFT%(1,I),left%(2,i)*3.5,0,0

That 3.5 really ought to have been a variable… because in practice it defines the tempo.

By hard-coding it, I locked the whole arrangement to one playback speed. That was good enough for getting the tune working, but it was the wrong design choice. A proper version should have used something like a tempo! or speed! variable so the same compiled note data could be played faster or slower without editing the code.

So yes… it worked.

But it also meant the musical timing and the playback tempo were more tightly coupled than they should have been.

That is very typical of quick utility code written to get a job done… perfectly serviceable in the moment, but with a design shortcut that becomes obvious thirty-odd years later.

It still has that very honest old-code feel

Looking through the listing now, I can also see the usual signs of real working code rather than something tidied up for publication.

Some variable names are functional rather than elegant. Some comments and channel labels drift a little. There are traces of experimentation and practicality all over it.

I rather like that.

It reminds me that this was not written to impress anyone on the internet in 2026. It was written because I needed to solve a problem in 1992.

That is a very different mindset.

There was no thought of a future blog post, a YouTube video, or code archaeology thirty-four years later…

Just a job to do, a machine to make it work on, and enough curiosity to have some fun with it along the way.

Temporary programs have a habit of becoming interesting

What started as a utility to encode music for Money Matters ended up containing quite a few ideas packed into one BASIC listing:

  • a small symbolic music format
  • a parser for notes, sharps, octaves and rests
  • mathematical pitch conversion
  • precompiled score arrays
  • queue-driven three-channel playback
  • interrupt-based scheduling
  • a live keyboard test to prove the music kept running independently

Not bad for something that was only meant to be temporary.

There is a lesson in that somewhere…

Sometimes the tools we write for ourselves end up being just as interesting as the main project they were built to support.

And sometimes, decades later, they become a little time capsule of how we thought, how we solved problems, and how much you could squeeze out of an 8-bit machine with a bit of patience and a lot of fiddling.

Looking back at this one now, I am rather glad I kept it.

Because hidden inside a practical little CPC utility is a snapshot of younger me… trying to teach a computer to play a tune, while proving at the same time that BASIC interrupts really could keep the music going in the background.

That still feels quite satisfying.

Source Code

If you don’t want to check out my Github Repo, you can find the original source code from 1992 below.

1000 ' MELODY/BASS LINE Generator Created By Jason Brooks
1010 ' (C) 1992 JacesofT Software Ltd
1020 DIM left%(2,300),right%(2,300),middle%(3,300)
1030 DEF FNperiod%(note%)=ROUND(62500/(440*(2^(oct%+((note%-10)/12)))))
1040 tone$=" C C#D D#E F F#G G#A A#B R"
1045 ENV 1,1,15,1,3,-1,1,5,0,1,12,-1,5
1050 ' Sort Out Melody Line Data
1052 PRINT"Coding CHANNEL A:"
1055 pass=1:pointer%=1
1060 RESTORE 10000:oct%=0
1080 GOSUB 4000:'Decode note data
1090 IF note$="X" THEN 1120
1100 left%(1,pointer%)=note%:left%(2,pointer%)=dur%:pointer%=pointer%+1
1110 GOTO 1080
1120 pass=pass+1:IF pass=2 THEN 1080
1130 IF pass=3 THEN 1060
1140 IF PASS=4 THEN RESTORE 10110:GOTO 1080
1150 fa=pointer%
1200 ' Sort out BASS line (Right) channel music data.
1205 PRINT"Coding CHANNEL B:"
1210 pass=1:point%=1
1220 RESTORE 11000:oct%=-1
1230 GOSUB 4000
1240 IF note$="X" THEN 1270
1250 right%(1,point%)=note%:right%(2,point%)=dur%:point%=point%+1
1260 GOTO 1230
1270 pass=pass+1:IF pass=2 THEN 1230
1280 IF pass=3 THEN 1220
1290 IF pass=4 THEN RESTORE 11170:GOTO 1230
1300 fc=point%
1500 ' Sort out Melody/Bass Line (Middle) channel music data.
1510 PRINT"Coding CHANNEL C:":pass=1:point%=1
1520 RESTORE 12000
1530 GOSUB 4000
1540 IF note$="X" THEN 1570
1550 middle%(1,point%)=note%:middle%(2,point%)=dur%:point%=point%+1
1560 GOTO 1530
1570 pass=pass+1:IF pass=2 THEN 1530
1580 IF pass=3 THEN 1520
1590 IF pass=4 THEN RESTORE 12160:GOTO 1530
1600 fb=point%
3000 GOTO 8000
3999 STOP
4000 ' Decode Music Data
4010 READ note$:IF note$="X" THEN RETURN
4020 IF note$="R" THEN READ dur%:note%=0:RETURN
4030 READ dur%:IF LEN(note$)=1 THEN 4100
4040 ON LEN(note$)-1 GOSUB 4200,4300,4400
4100 tone%=INSTR(tone$,note$)/2:note%=FNperiod%(tone%):RETURN
4200 l$=RIGHT$(note$,1):IF l$="#" THEN RETURN ELSE oct%=VAL(l$):note$=LEFT$(note$,1)
4210 RETURN
4300 l$=RIGHT$(note$,2):IF LEFT$(l$,1)="#" THEN oct%=VAL(RIGHT$(l$,1)):note$=LEFT$(note$,2):RETURN
4310 note$=LEFT$(note$,1):oct%=VAL(RIGHT$(l$,2)):RETURN
4400 oct%=VAL(RIGHT$(note$,2)):note$=LEFT$(note$,2):RETURN
8000 ' Try All Channels Music Driver
8010 pta=2:ptb=2:ptc=2
8020 SOUND &39,left%(1,1),left%(2,1)*3.5,0,1
8025 SOUND &3A,middle%(1,1),middle%(2,1)*3.5,0,0
8030 SOUND &3C,right%(1,1),right%(2,1)*3.5,0,1
8040 EI:EVERY 1 GOSUB 8100
8050 a$=INKEY$:IF a$="" THEN 8050 ELSE PRINT a$;:GOTO 8050
8100 ON SQ(1) GOSUB 8200
8110 ON SQ(4) GOSUB 8300
8120 ON SQ(2) GOSUB 8400
8122 IF NOT(FC=PTC AND FA=PTA AND fb=ptb) THEN RETURN
8123 SOUND &X111111,0,100,0,0:PTA=1:PTC=1:PTB=1
8130 RETURN
8190 ' Channel A
8200 DI:IF pta>=fa THEN RETURN
8205 IF LEFT%(1,pta)<>0 THEN SOUND 1,LEFT%(1,pta),left%(2,pta)*3.5,0,1 ELSE SOUND 1,LEFT%(1,pta),left%(2,pta)*3.5,0,0
8210 pta=pta+1
8220 RETURN
8290 ' Sound ON Channel C
8300 DI:IF ptc>=fc THEN RETURN
8310 IF right%(1,ptc)<>0 THEN SOUND 4,right%(1,ptc),right%(2,ptc)*3.5,0,1 ELSE SOUND 4,right%(1,ptc),right%(2,ptc)*3.5,0,0
8320 PTC=PTC+1:RETURN
8399 ' Sound ON Channel B
8400 DI:IF ptb>=fb THEN RETURN
8410 ev=1:IF middle%(1,ptb)=0 THEN ev=0
8420 SOUND 2,middle%(1,ptb),middle%(2,ptb)*3.5,0,ev:ptb=ptb+1:RETURN
9000 ' TEST MUSIC SCORE
9010 FOR I=1 TO POINTER%
9020 IF LEFT%(1,I)<>0 THEN SOUND 1,LEFT%(1,I),left%(2,i)*3.5,0,1 ELSE SOUND 1,LEFT%(1,I),left%(2,i)*3.5,0,0
9030 NEXT
10000 ' Music Data For CHANNEL A (All Envelope 1, Piano.)
10010 DATA G0,4,F,4,G,4,F,4,E,8,C,8
10020 DATA R,8,E,4,F,4,G,4,F,4,G,4,F,4
10030 DATA E,4,F,4,G,4,A,4,A#,4,A,4,A#,4,A,4
10040 DATA G,8,R,8,R,16
10050 DATA G#,8,G,8,F#,8,F,8
10060 DATA D#,4,D,4,C,4,D,4,D#,8,R,8
10065 DATA X
10066 ' Music For First Pass Around
10070 DATA D#,4,D,4,C,4,D,4,D#,8,C,8
10080 DATA G,8,R,8,F#1,1,G,7,R,8
10100 DATA X
10101 ' Music For Second Time Around
10110 DATA D#0,4,D,4,C,4,D#,4,D,4,C,4,B-1,4,D0,4
10120 DATA C,8,E,8,F,8,G,8
10130 DATA A,8,A,8,A,8,A,8
10140 DATA A,8,B,4,C1,4,B0,8,A,8
10150 DATA G,8,G,4,G,4,G,4,G,4,G,4,G,4
10160 DATA G,24,G,8
10170 DATA F,8,F,8,F,8,F,8
10180 DATA F,8,G,4,A,4,G,12,F,4
10190 'SCORE 4
10200 DATA E,32
10210 DATA C1,8,E0,8,F,8,G,8
10220 DATA G#,8,G#,8,G#,8,G#,8
10230 DATA G#,8,A#,4,C1,4,A#0,8,G#,8
10240 DATA G,8,G,4,G,4,G,4,G,4,G,4,G,4
10250 DATA G,32
10260 'SCORE 5
10270 DATA F0,8,F,8,F,8,F,8
10280 DATA D#,8,D,8,C,12,D#,4
10290 DATA G,32
10300 DATA G,16,R,16
10310 DATA G,4,F,4,G,4,F,4,E,8,C,8
10320 DATA R,8,E,4,F,4,G,4,F,4,G,4,F,4
10330 'SCORE 6
10340 DATA E,4,F,4,G,4,A,4,A#,4,A,4,A#,4,A,4
10350 DATA G,8,R,8,R,16
10360 DATA G#,8,G,8,F#,8,F,8
10370 DATA D#,4,D,4,C,4,D,4,D#,8,R,8
10380 DATA G#,8,G,8,F#,8,F,8
10390 'SCORE 7
10400 DATA D#,4,D,4,C,4,D,4,D#,16
10410 DATA G#,8,G,8,F#,8,F,8
10420 DATA D#,4,D,4,C,4,D,4,D#,16
10430 DATA D#,4,D,4,C,4,D#,4,G,8,B,8
10440 DATA C1,8,R,8,R,16
10450 DATA X
11000 ' Music Data for CHANNEL C (Right) - Bass Line
11010 ' SCORE 1
11020 'DATA C-1,8,C0,8,C-1,8,E,-1
11030 'DATA C-1,8,G,8,C,8,C0,8
11040 DATA C-1,8,R,8,C-1,8,R,8
11050 DATA C-1,8,R,8,C,8,R,8
11060 DATA C-1,8,R,8,C,8,R,8
11070 DATA C-1,8,R,8,C,8,R,8
11080 DATA G-2,8,R,8,G,8,R,8
11090 ' SCORE 2
11100 DATA C-2,8,R,8,C-1,8,R,8
11110 DATA X
11120 ' SCORE FOR FIRST PASS CHANNEL C
11130 DATA A-2,32
11140 DATA G,8,R,8,B-3,8,R,8
11150 DATA X
11160 ' SCORE FOR SECOND PASS CHANNEL C
11170 DATA A-2,8,R,8,G-2,8,R,8
11180 DATA C-1,8,R,8,C,16
11190 ' SCORE 3
11200 DATA F-1,32
11210 DATA D,16,G-2,8,R,8
11220 DATA C-1,8,G,8,G-2,8,G-1,8
11230 DATA A-2,8,E-1,8,A,8,R,8
11240 DATA D-1,8,R,8,D-2,8,R,8
11250 DATA G-2,16,G,8,R,8
11260 ' SCORE 4
11270 DATA R,8,C-1,4,G-2,4,E-1,4,C,4,G,4,E,4
11280 DATA C0,8,R,8,C-1,16
11290 DATA F,32
11300 DATA D,16,A#-2,8,R,8
11310 DATA D#-1,8,A#,8,A#-2,8,R,8
11320 DATA G,8,R,8,C-1,8,R,8
11330 ' SCORE 5
11340 DATA F-2,32
11350 DATA F#,32
11360 DATA R,8,C#-1,8,D,8,E,8
11370 DATA F,16,A-3,8,R,8
11380 DATA C-1,8,R,8,C,8,R,8
11390 DATA C,8,R,8,C,8,R,8
11400 ' SCORE 6
11410 DATA C-1,8,R,8,C,8,R,8
11420 DATA C,8,R,8,C,8,R,8
11430 DATA G-2,8,R,8,G,8,R,8
11440 DATA C,8,R,8,C-1,8,R,8
11450 DATA G-2,32
11460 ' SCORE 7
11470 DATA C-1,24,C-2,8
11480 DATA G,32
11490 DATA C-1,8,A#-2,8,A,16
11500 DATA F#,8,R,8,G,8,R,8
11510 DATA C-1,8,G-2,8,C,8,R,8
11520 DATA X
11999 DATA X
12000 ' Music Data For CHANNEL C: (Melody/Bass Lines) - Middle
12010 ' SCORE 1
12020 DATA R,8,C0,8,R,8,G-1,8
12030 DATA R,8,C0,8,R,8,E,8
12040 DATA R,8,C,8,R,8,C,8
12060 DATA R,8,C,8,R,8,C,8
12070 DATA F0,8,E,8,D#,8,D,8
12080 ' SCORE 2
12090 DATA G-1,4,R,20,C0,8
12100 DATA X
12110 ' FIRST PASS
12120 DATA F#-1,32
12130 DATA F,8,R,24
12140 DATA X
12150 ' SECOND PASS
12160 DATA F#-1,8,R,8,F,8,R,8
12170 DATA E,8,R,8,A#,16
12180 ' SCORE 3
12190 DATA R,8,C0,8,R,8,C,8
12200 DATA F,8,G,4,A,4,G,8,F,8
12210 DATA E,8,R,8,E,16
12220 DATA C#,24,R,8
12230 DATA R,8,A-1,8,R,8,A,8
12240 DATA B-1,16,R,8,B,8
12250 ' SCORE 4
12260 DATA C0,32
12270 DATA E,8,E,8,D,8,E,8
12280 DATA R,8,F,8,R,8,F,8
12290 DATA F,8,G,4,G#,4,G,8,F,8
12300 DATA D#,16,D,16
12310 DATA R,8,A#-1,8,R,8,A#-1,8
12320 ' SCORE 5
12330 DATA R,8,C0,8,R,8,C0,8
12340 DATA A-1,24,A,8
12350 DATA R,8,A#,8,B,8,C#0,8
12360 DATA D0,16,R,16
12370 DATA R,8,C,8,R,8,C,8
12380 DATA R,8,C,8,R,8,C,8
12390 ' SCORE 6
12400 DATA R,8,C,8,R,8,C,8
12410 DATA R,8,C,8,R,8,C,8
12420 DATA F,8,E,8,D#,8,D,8
12430 DATA G-1,8,R,16,G,8
12440 DATA B,32
12450 ' SCORE 7
12460 DATA G-1,24,R,8
12470 DATA F,32
12480 DATA G,16,G,16
12490 DATA C,8,R,8,F,8,R,8
12500 DATA E,8,R,24
13000 DATA X

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.