Log inRegister an accountBrowse CSDbHelp & documentationFacts & StatisticsThe forumsAvailable RSS-feeds on CSDbSupport CSDb Commodore 64 Scene Database
 Welcome to our latest new user jobj ! (Registered 2024-12-25) You are not logged in - nap
CSDb User Forums


Forums > C64 Composing > Vicious Routine
2009-12-23 13:24
Enth
Account closed

Registered: Sep 2009
Posts: 3
Vicious Routine

Has this topic been covered here yet?

Vandalism News #50 (http://www.pouet.net/prod.php?which=52467)
(You need to switch to disk #2 after you've selected the article in question, no prompt to do so at least with emulator...)

Discussion about PWM and samples that are referred at the beginning of the article:
http://www.ffd2.com/fridge/chacking/c=hacking20.txt "The C64 Digi"
http://www.ffd2.com/fridge/chacking/c=hacking21.txt "Pulse Width Modulation, continued"
.
.
.
Vicious Routine

In this article I will describe a new kind of sample player routine which can play crystal clear 8 bit samples! Even with the screen turned on. This is the routine that was used in our demo Vicious Sid from X2008. It is based on the idea of using SID waveforms to construct the samples.

Okay, you can't call 8bit,8kHz crystal clear, but at least this method sounds much better than playing samples using the volume register ($D148) or the method using pulse width modulation. The problem with $D148 samples is that they're limited to 4 bits. With pulse width modulation (PWM) you can achieve 12 bit samples but instead you will hear a very loud and ear-piercing 4khz tone together with the played sample. There are other less known PWM based methods that produce pretty good quality samples, like the one by Ninja in Darwin. I believe this is the same method or similar to the method invented by the Levente Harsfalvi presented in C=Hacking #21. The drawback is that these routines cost you all the raster time.

-THE PROBLEM-

The idea for this kind of routine isn't completely new. I know that at least Krill and Agemixer have been planning on using fragments of SID waveforms for creating more complex waveforms after on IRC discussion on #c-64 with Exin and Necronomfive about the possibility to play samples by utilising SID waveforms I came up with a new idea.

As I see it, the biggest problem with creating more complex waveforms by utilising the xisting SID waveforms is that you'd want to quickly jump to specific positions in the SID waveforms. The SID doesen't have a mechanism that allows this. The only way is all the way through the whole waveform. It takes circa 256 cycles at frequency $ffff to play one period of a waveform, which is simply not fast enough.

For example, one idea would be to use the saw waveform to set the desired DAC level for each sample. We could do this by resetting the oscillator of a SID voice with the test bit. Then we could run it until it reaches the desired level and stop it by setting its frequency to $0000. However, the SID is too slow for this purpose.

It would now be easy to conclude that playing samples by using SID waveforms isn't possible; the oscillators are too slow, and we're stuck at a specific position in a waveform too long.

However, usually these kinds of statements contain some untrue assumptions. It's the beauty of C64 coding to do seemingly impossible things.

I will start by presenting an implementation that is easy to understand and easy to implement. I will then present a more advanced method.

-THE FIRST IMPLEMENTION-

The problem was to overcome the slowness of the SID oscillators. Surely it's not possible to overclock the SID with software...But who said we are limited to only once? My solution was to use two oscillators instead of one. It might be impossible to play samples with only one voice, but what about two voices? The trick is to have one voice sounding while the other one is "seeking" the right level for the next sample.

Just like in the example with the saw waveform mentioned earlier, but with two voices. When it is time to play the next sample, the roles of the voices are swapped. This way the transition from the current sample level to the next doesen't have to fast, and oscillator slowness isn't a problem.

By seeking I mean running trhe oscillator until it reaches the desired sample level. The oscillator of a SID voice is a 24bit register, to which the frequency value is added every cycle. This register is called the phase accumulator. All waveforms (expect noise) are generated by teh help of the value in the phase accumulator. The waveform is generated by outputting the value of the phase accumulator itself. This means that we can send the value in the phase accumulator to the DAC ("play it") by just toggling on the saw waveform.

Seeking is accomplished like this:

1) Clear the value in the phase accumulator by setting the test bit
2) Set the FREQUENCY of the oscillator to the sample level we wish to seek
3) Start the oscillator (clear the test bit) and siable output (waveform #00)
4) Wait a CONSTANT amount of time (typically two raster lines)

As already mentioned, the frequency value is added to the phase accumulator each cycle. So after N cycles the value in the phase accumulator will be N*frequency. In other words, throwing the desired sample level to the frequency register actually works (step2), and the longer we wait (step 4) the ounder the playback will be. The delay in step 4 has to be to 256 cycles or less. Otherwise the value in the phase accumulator might wrap.

Now that the phase accumulator contains the desired sample level it can be "played":

5) Stop the oscillator by setting its frequency to 0
6) Select the saw waveform (triangle waveform works as well)

If the delay in step 4 is 128 cycles or less, it's usually better to use the triangle waveform instead of the saw waveform. The triangle waveform is similar to the saw waveform, but rises twice as fast. Hence using the triangle waveform means doubling the volume. The maximal delay in step4 is then 128 cycles, since the latter half of the triangle waveform is unusable.

So code doing this with two voices couldlook something like this:

IRQ1:
STA $FE
STY $FF

LDA #$00 ;step 5(voice 3)
STA $D40F
LDA #$11 ;step 6(voice 3)
STA $D412

LDA #$09 ;step 1(voice 2)
STA $D40B
LDY #$00 ;step 2(voice 2)
LDA ($10),Y
STA $D408
LDA #$00 ;step 3(voice 2)
STA $D40B

;step 4 until next irq (voice 2)

INC $10
BNE *+4
INC $11
LDA $DC0D

LDA #<IRQ2
STA $FFFE
LDA #>IRQ2
STA $FFFF

LDA $FE
LDY $FF
RTI

IRQ2:
STA $FE
STY $FF

LDA #$00 ;step 5(voice 2)
STA $D408
LDA #$11 ;step 6(voice 2)
STA $D40B

LDA #$09 ;step 1(voice 3)
STA $D412
LDY #$00 ;step 2(voice 3)
LDA ($10),Y
STA $D40F
LDA #$00 ;step 3(voice 3)
STA $D412

;step 4 until next irq (voice 3)

INC $10
BNE *+4
INC $11
LDA $DC0D

LDA #<IRQ1
STA $FFFE
LDA #>IRQ1
STA $FFFF

LDA $FE
LDY $FF
RTI

As you can see, there's no waiting (step 4) inside the IRQ. The waiting happens outside the IRQ, in order to save raster time. This was suggested by Mixer. At first I was against it because having an unstable raster would result in a delay that would vary. I thought this would add a high pitched carrier noise to the sound. However, this doesen't happen. The reason will be explained later. The beauty of this implementation is indeed that a stable raster isn't required.

-THE SECOND IMPLEMENTATION-

This version is a bit trickier due to raster timing. A stable raster is required. The idea is pretty much the same as for the first implementation.

So I stated that it might be impossible (what would Crossbow do) to play samples by using just one voice due to oscillator slowness. Ironically, it tunrs out that even the assumption about two channels being required is false. This is because it's actually possible to play AND seek simultaneously on a single SID voice. Onl Amig... C64 makes it possible.

How can this be possible? It's done by using no waveforms at all! In the first implementation we used waveform $00 to "mute" a voice. Initially I thought this would result in a zero level output from the DAC. Later I found out what really happens is that the current waveform level is sustained (the effect lasts a few seconds, after this the DAC level will decay to 0).

Knowing this, we can use the following method:

1) Start playing a sample (enable saw/triangle)
2) Sustain the current DAC level (disable saw/triangle output)
3) Reset phase accu (set test bit)
4) Set the FREQUENCY of the oscillator to the sample level we wish to seek
5) Start seeking (clear test bit) and wait...

And in code:

IRQ STA $FF

LDA $DC04
EOR #$07
AND #$07
STA *+4
BPL *
CMP #$C9
CMP #$C9
CMP #$24
NOP

LDA #$11 ;step 1
STA $D412

LDA #$09 ;step 2 & 3
STA $D412

IRQ1 LDA $1000 ;step 4
STA $D40F

LDA #$01 ;step 5
STA $D412

INC IRQ1+1
BNE IRQ2
INC IRQ1+2

IRQ2 LDA $DC0D
LDA $FF
RTI

This method is much trickier to implement and hence I recommend starting with the two voice version. It should b easier to modify a two voice version to a one voice version than writing it from scratch. The timing is very crucial and trhe number of cycles is very limited on badlines. Try inserting a few instructions in the aboce routine and it won't work anymore.

The LDA $DC04 thing in the beginning of the IRQ routine makes the raster stable. The idea is to read the current position in the rasterline from $DC04 and synch according to that. I'm not going to describe how it works in detail here. There's also a faster but more complicated way to do the stable raster code with the CIA. An example on gow to do it can be found at:
http://www.dekadence64.org/prodstools.html

-EXAMINING WAVEFORM $00-

You can use this little program to watch the effect of waveform $00. It will first set the voice output to $ff and then toggle waveform $00. As can be seen from the screen, the output of the voice will "fade" from $ff to $00 within some seconds.

LDA #$00
STA $D410
STA $D411
LDA #$40
STA $D412
LDA #$00
STA $D412

LOOP LDA $D41B
STA $0400
JMP LOOP

You might wonder why you don't get a very noticeable carrier with the two voice version, even though your raster isn't stable. The running time ofd the oscillator, and as a result, the amplitude will vary which indeed DOES produce carrier noise.

Since the two version "mutes" the channel by setting waveform $00, the effect is that the current and the previous samples are played simultaneously. The averaging of the samples will smoothen the waveform and remove sharp transitions. As a result the played sample gets low pass filtered and the high pitched carrier noise disappears. Remember that high frequencies are represented by sharp transitions in the sample data. This also means that the one voice version sounds clearer since it's not low pass filtered.

To my knowledge the only emulator supporting the sustaining bevaviour of waveform $00 is Hoxs64. I also know that Antti Lankila added this behavious to his patched ReSID version. On other emulatoes both the 1 and 2 voice versions will sound very poor.

-CONCLUSION-

The disadvantage of this method is of course that you lose at least one SID voice. But in exchange for that you get clearer sample replay than with other methods. Another advantage compared to $D418 samples is that the method works on both 6581 and 8580.

Since the samples are generated with SID voices, it's also possible to filter the voice(s) or to do volume tricks with the ADSR. Mixer did a good job of demonstrating this in Vicious Sid.

It should also be possible to make a 12 bit routine, although I'm not sure it would be worth it due to the lost memory and added complexity.

The waveform DAC of the SID is quite non-linear. In order to improve the sound quality this nonlinearity would need to be examined. I suspect the non-linearity can vary quite a lot between various SIDs, so compensating for it might not be trivial (remember how some 6581 SIDs sound satured even without any filtering, while others sound clean).

I'm providing some example code for your pleasure. SInce I'm suspecting almost no one's using TASS these days, I won't bother including sources on this disk. Instead you can download some example sources in text format from (not yet there, will be soonish):
http://www.dekadence64.org/prodstools.html

As a punisment for your cross compiler lameness the sources will be in traditional C64 TASS format. ;) You will probably have to make some small changes in order to adapt them to your assembler.

Happy hacking,
SounDemon.
2009-12-23 15:57
Oswald

Registered: Apr 2002
Posts: 5094
great, adding it to codebase right now. :)
2009-12-24 12:15
linde

Registered: Jul 2006
Posts: 47
Thanks, that's a nice write-up. This way, it should be possible to ring modulate a sample as well, right?
RefreshSubscribe to this thread:

You need to be logged in to post in the forum.

Search the forum:
Search   for   in  
All times are CET.
Search CSDb
Advanced
Users Online
hedning/G★P
JEZ
Sychamis
Andy/AEG
Magic/Nah-Kolor
sachy/BOOM!
Six/G★P
kbs/Pht/Lxt
Didi/Laxity
Stinsen/G★P
Guests online: 118
Top Demos
1 Next Level  (9.7)
2 13:37  (9.7)
3 Mojo  (9.7)
4 Coma Light 13  (9.6)
5 The Demo Coder  (9.6)
6 Edge of Disgrace  (9.6)
7 What Is The Matrix 2  (9.6)
8 Uncensored  (9.6)
9 Comaland 100%  (9.6)
10 Wonderland XIV  (9.6)
Top onefile Demos
1 Layers  (9.6)
2 Cubic Dream  (9.6)
3 Party Elk 2  (9.6)
4 X-Mas Demo 2024  (9.6)
5 Copper Booze  (9.6)
6 Dawnfall V1.1  (9.5)
7 Rainbow Connection  (9.5)
8 Onscreen 5k  (9.5)
9 Morph  (9.5)
10 Libertongo  (9.5)
Top Groups
1 Booze Design  (9.3)
2 Oxyron  (9.3)
3 Performers  (9.3)
4 Censor Design  (9.3)
5 Triad  (9.3)
Top Graphicians
1 Mirage  (9.8)
2 Archmage  (9.7)
3 Pal  (9.6)
4 Carrion  (9.6)
5 Sulevi  (9.6)

Home - Disclaimer
Copyright © No Name 2001-2024
Page generated in: 0.119 sec.