I'm trying to write a program, in Basic that allows me to play some music using the Sid. The music has to be stored somewhere as a songsheet, which means notes with tempo and their pitch.
To do that I must consider three things:
Fact 1.
The SID has three channels, which can operate indipendently from each other.They map their registers on similar layouts on different memory addresses, where we can POKE the values in.
Fact 2.
Each note I want to play can have a different timing from the others.
Fact 3.
Save building a multitasking os for the C64 (lol, nope, or at least, not yet) I have only one cycle available to read data.
The question becomes: how do I store the data and how I load it into the SID registers by using only one program loop?
I think I will just do something like this. I read the data about the three notes. The data being laid out like this.
[note-1-pitch],[note-1-delay],[note-2-pitch],[note-2-delay],[note-3-pitch],[note-3-delay]
I will have some counters, independent from the SID, that will tell my loop if the time has come to read another note from the DATA directives. What I'm thinking is about a mechanism that works like this "If I'm ready to retrieve note-1 (or 2 or 3) I will read it, play it and update the corresponding counter". I will be able to mix different notes in the same bus.
[note-1-pitch],[note-1-delay],[note-1-pitch],[note-1-delay],[note-2-pitch],[note-2-delay],[note-1-pitch],[note-1-delay],
[note-3-pitch],[note-3-delay]
When I put a [-1][-1] as data I will stop a channel, when all the channels are stopped then I will reset the SID and exit the application.
My workflow is something like this. Note that I use labels instead of numbering my statements, and I use the # instead of REM for labeling my comments.
GOSUB SET-UP-SID
# we set up the continue variables here
# we want to have them visible in the main loop
CO% = 1 #
# we can also initialize further variables here
[...]
LOOP:
# we can also initialize further variables here
[...]
LOOP:
# this is how we can emulate a while-true cycle in basic
IF CO% = 0 GOTO END-LOOP
# now we will read the notes and upload them to the correct channel
[...]
# now we will wait 1/4 of a second
[...]
# and after it we jump back
[...]
# and after it we jump back
GOTO LOOP
END-LOOP:
GOSUB RESET-SID
END
SET-UP-SID:
# rem here we will set up the channels with the correct instruments
[...]
[...]
RETURN
RESET-SID:
# rem here we will clear the SID
[...]
[...]
RETURN
As you notice there are some empty boxes, but we are going to fill them up.
As you notice there are some empty boxes, but we are going to fill them up.
Setting up the SID
First we define some helper variables to keep memory locations we need to address
SV=54296 # sid volume register
W1=54276 # adsr/waveform for voice 1
W2=54283
W3=54290
A1=54277 # attack/decay for voice 1
A2=54284
A3=54291
S1=54278 # sustain/release for voice 1
S2=54285
S3=54292
Next we will give up the right values we want to give them
POKE SV,15 # maximum volume
# We turn Sid Channel 1 into Flute
POKE W1,17 # triangle waveform
POKE A1,96
POKE S1,0
# We turn Sid Channel 2 into Harpsichord
POKE W2,33 # sawtooth waveform
POKE A2,9
POKE S2,0
# We turn Sid Channel 3 into Organ
POKE W3,17 # triangle waveform
POKE A3,0
POKE S3,240
Resetting the SID
In the end we want to set the volume back to zero.
POKE SV,0
It might also be a good thing to zero all the values we put in the various SID registers. Since SID registers are contiguous we can just use a for loop initialized to the minimum register to the upper register
FOR I = W1 TO S3
POKE I,0
NEXT
No comments:
Post a Comment