Jump to content
pavros

POKEY's C distortion explained

Recommended Posts

Some time ago I finished working on a document where I describe results of my research on the POKEY's C distortion. It says about the rules related to generating the distortion as well as about the ways to stabilize bass sounds referred to as C bass (or "Donkey Kong" bass by Synthpopalooza). The instability issue has been already mentioned in the threads:
http://atariage.com/forums/topic/116835-polycounter-reset-on-pokey
http://atariage.com/forums/topic/212280-pokey-question-distortion-12-donkey-kong-bass
Beside the document there is an excel file attached, which is a supplement for the study and contains POKEY frequency/note tables for all its clocks.
There are also slides which I was presenting on WAPNIAK party in 2017 and a Bass Tester program which allows you to play a bit with the C distortion in practice. The program is compiled in three versions differing in the method of counter synchronization - two variants of first method and the second metod. The Bass Tester is provided together with its complete source code. On screen you will see what keys can be used for various settings. The manual is actually included in the slides. If used under the Altirra emulator, it's worth turning on the Audio Monitor to watch oscillograms. What is not mentioned in the manual is key 0 which toggles accurate phase modulation.
I hope you will find it all useful.
 

c-distortion.zip

  • Like 12
  • Thanks 1

Share this post


Link to post
Share on other sites

Ok, let's continue here, it's your research 🙂

 

First let me thank you once more for this great research, it really brings lot of details about little explored subject.

 

After finally reading Atari Fan article I did some experimenting on my own, and I found there is still some clicking when changing AUDF registers.

 

CBass1.xex

 

It only happens sometimes, let the scale play several times, you will know when you hear it.

 

This code uses first synchronization method, on the beginning only. After that, it just relies on playing just frequencies which are multiple of 3. It uses normal RMT player with RMT C distortion and normal RMT frequency table for C.

 

The clicking is not related to STIMER synchronization, as no synchronization is done after the initial one. It is caused by the fact, that while all frequencies play 10000 bit sequence, those bits are played in different order for each multiple of 3. So when you change frequency between bits, there might be slight phase shift, which produces the clicks.

 

It's not big issue in most cases, and I don't think it can't be easily solved, but I wonder what do you think about it.

 

Edited by R0ger

Share this post


Link to post
Share on other sites
51 minutes ago, R0ger said:

 

CBass1.xex 1.87 kB · 2 downloads

 

It only happens sometimes, let the scale play several times, you will know when you hear it.

I first ran it on atari800. There it sounds unstable (which shows that its Pokey emulation is sub-par to Altirra), but no clicks(!). After that, I tried Altirra and it's stable, but indeed after a few rounds of the scale you hear a slight click. I wonder if it isn't related to the RMT player somehow? I find it very hard to believe that a single phase shift combined with a frequency change at the same time can create such an audible click.

 

 

Share this post


Link to post
Share on other sites
7 minutes ago, ivop said:

I first ran it on atari800. There it sounds unstable (which shows that its Pokey emulation is sub-par to Altirra), but no clicks(!). After that, I tried Altirra and it's stable, but indeed after a few rounds of the scale you hear a slight click. I wonder if it isn't related to the RMT player somehow? I find it very hard to believe that a single phase shift combined with a frequency change at the same time can create such an audible click.

 

 

It's not related to RMT, Here it is without RMT, just one write to AUDF every time the frequency changes.

 

CBass.xex

 

I wasn't sure how the click happens either. But I recorder it from Altirra, and then analyzed the recording in Audacity, and it's clearly just the phase shift, and nothing else.

 

Basically you get sequence like this: 1000010000101000010000 ... that is enough to produce short transient higher harmonics pulse.

 

Here's how it looks as recording:

 

image.thumb.png.d0bff70b9cdd2304a1746bcfd06472ca.png

 

Btw. it seems like this 0010100 is one of the most audible cases. It of course depends on what frequencies are being exchanged and at what point the the sequence it happens.

Edited by R0ger
  • Like 2

Share this post


Link to post
Share on other sites

Recording from an NTSC 130XE:

 

CBass1.thumb.png.266bc6e48ad2b02783659a8b88ff9b4d.png

 

Depending on the phase change, yeah, you're going to occasionally get two pulses close to each other. Emulation of this is going to be very touchy as the output depends on emulation of the correct polynomial offsets coming out of init mode, the correct polynomial patterns, and the correct relative delays between the channels tapping off from the global polynomial counters.

CBass1.wav

  • Like 2

Share this post


Link to post
Share on other sites
26 minutes ago, phaeron said:

Recording from an NTSC 130XE:

 

CBass1.thumb.png.266bc6e48ad2b02783659a8b88ff9b4d.png

 

Depending on the phase change, yeah, you're going to occasionally get two pulses close to each other. Emulation of this is going to be very touchy as the output depends on emulation of the correct polynomial offsets coming out of init mode, the correct polynomial patterns, and the correct relative delays between the channels tapping off from the global polynomial counters.

CBass1.wav 591.79 kB · 1 download

Somehow Altirra manages to do that quite well. As if it was made by some dedicated madman :-D

Problem is Atari 800 (hence Coleen) or RMT are way behind, and most of this advanced stuff is just beyond their reach. Sure, RMT uses external pokey emulation, but the way it calls it clearly has some limitations to precision. That's certainly another area where it can be improved.

 

 

 

 

  • Like 1
  • Haha 1

Share this post


Link to post
Share on other sites

Hi Guys,

You are absolutely right about the reason of the click. Indeed, two subsequent pulses may happen one after another closer than expected as the order of sampled elements of poly4 sequence changes depending on the frequency setting.
What is also very important, we need to remember that the moment when a timer reloads with new frequency setting is different than the moment when we put a new frequency value to AUDFx register. The delay from register's modification to actual timer reloading may take up to whole sampling cycle i.e. 1/5 of the C distortion wave period of previously set frequency. This delay also has an impact on which bit/element of poly4 sequence will be sampled first after changing the frequency.
I assume that we modify POKEY registers at each VBLK interrupt (so with 50 or 60Hz rate). Let's call a single 20ms period between two subsequent VBLKs a frame. Let's also assume that we are mostly interested in very low frequencies 50-100Hz and we are only interested in sound of T1 timbre (with a pulse lasting 20% of cycle time).
The solution would be eliminating the next pulse of the T1.
Unfortunately there is no good method for doing that.
As mentioned by Rybags, one of the solutions is to decrease the volume of the signal for one frame. This solution is actually good for channel 1 and 2 only. As channels 3 and 4 have negated output and for 4/5 of each period a high state is sustained, setting the volume to 0 during the single frame will cause a 20ms negative pulse which will result in a click. Even if this method is applied to channel 1 or 2, there may still an issue happen. When the volume is restored after one frame, it may coincide with another pulse and the effect will be reduced length of the pulse which may result in a click because of different set of generated harmonics. This only means that the method is not perfect.
There is another solution which could be applied to channels 3 and 4. This is using frequency divisors which cause poly4 "sampling pause" - the divisors which are multiples of 15. I refer to them in my article as divisors belonging to P4 set. The lowest value belonging to the P4 set is just 15, for which a value of 14 must be written to AUDFx register. Using this "sampling pause" effect we will mute the output of the channel. We should put a P4 value for two subsequent frames to guarantee that it will last for at least one frame. This is related to the possible delay between writting a value to AUDFx and reloading the timer with this value, what I've already mentioned. So after two frames where we write 14 to AUDFx we can only set it to desired frequency divisor. Unfortunately, this method is not perfect either. We may be unlucky and apply "sampling pause" during the pulse of T1. This will result in unwanted up to 40ms pulse (up to 2 frames). Still, we have a 20% chance it will happen. If we used a first method with setting volume to 0 we would get 100% change to generate a 20ms long pulse.
So, the reason why the second method is not perfect is because we don't know which element of poly4 sequence is currently sampled. We can only know it just after the synchronization of the counters but we don't want of course to use it on every note change.
Please experiment a bit with both methods and let me know the results.

  • Like 2

Share this post


Link to post
Share on other sites

Attaching an example of bass based on C distortion, which is cut out of sound track of IK+ game. It lasts for couple of minutes and loops. I also encourage you to play with bass tester from package attached to first post, especially with piano using chromatic scale (switched with key "9") and active accurate phase modulation (key "zero") while two channels are playing.

ikp_bass.xex

  • Like 2

Share this post


Link to post
Share on other sites

Interesting ideas. I've just studied how you use the multiple of 15 and IRQ to introduce the phase shift. That's exactly what insight into the underlying principles allows you to do. Perfect !

I didn't test anything more yet. I'm more and more convinced the click is not really an issue in most of the cases.

There is another thing which I want to try though. With one sync you can make all multiples of 3 to generate 10000 sequence. That is what all music players can do, and it is imho the most useful achievement.

If we go for sync at every note, using method 2 to avoid much worse STIMER clicks, I think we could achieve any desired sequence (of those possible) for any frequency. We would just need table of starting offsets for every frequency, and code which will be able to do it. It's of course much more complicated, but hey, that's the fun part. There are some better sounding ones I think. And we could actually get like 5 different stable timbres ? That would be cool.

 

Still I see many limitations. It's just 8 bit bass. It can do PWM, but with IRQ and extra channel. And if we go for IRQ, we can go all the way and use IRQ to generate the bass directly, see this recent thread: https://atariage.com/forums/topic/292342-new-bass-approach-rediscovered-in-old-song

Or going the Hard-bass way of constant-sample-frequency soft-synth.

 

I'm not giving up on this though, it certainly makes C bass better.

 

 

 

  • Like 4

Share this post


Link to post
Share on other sites

An addendum ... there is another setting that behaves like the $Cx 3-period bass.

 

To access it, you use $8x white noise,  set AUDCTL=$C0.  This turns on the 9-bit polycounter and clocks  channel 0 at 1.79 mhz.

 

You play frequencies where (freq+4) mod 7=0.

 

When you do, a buzzy tone is produced, but the timbre varies even more than $Cx depending on timing.  It's possible these are of a similar nature to $Cx in regards to timing issues.

 

I am working up a note table now. I think this might be one of the settings used in RMT.

  • Like 2

Share this post


Link to post
Share on other sites

There are also bass sounds in the $4x distortion which are accessed with the 1.79  mhz clock.  They are also accessed in a similar manner to $Cx ... (freq+1) mod 3=0 produces differing timbres from the others.  In 8-bit mode the range is about as good as $Cx.

  • Like 1

Share this post


Link to post
Share on other sites

So, I uploaded the table over on the 7800 forum:

 

 

Amongst the new additions are the $8x settings (in 8 and 16 bit mode) with AUDCTL=%1xxxxxxx (9-bit polycounter) plus a 1.79mhz clock on the channel being played.  These produce buzzy bass tones in periods of 7, and the timbres vary in a similar manner as those of distortion 12b.

 

Also in this document are Distortion 4a and 4b, with the 1.79 mhz clock.  These also produce buzzy bass tones.  Distortion 4b (like 12b) uses periods of 3, and the timbres of these also vary depending on the current polycounter.

 

I hope this information proves useful and helpful in experiments.

Edited by Synthpopalooza
  • Like 2

Share this post


Link to post
Share on other sites

It's some weird player I've never seen. It seems to use 16 bit bass.

Basically you would have to insert your sync code before it starts playing, and mind it has to be done in DMA free time, like in VBI.

Then you have to make the player play tone table you need. Which might be an issue here. You have to understand the whole player first.

I know RMT player. I know where it stores its tone tables. I know where it defines distortion values for each instrument. So hacks like these are easy.

This thing, not so much.

Share this post


Link to post
Share on other sites

I can explain some of it.

 

In the .s code, there are tables.  A channel table determines the channel ($00 to $03) of each tune in the index.  CNTVOLTBL determines the AUDCx settings for each tune.  My music uses 4 tracks so each track has an index in the tune table. So the CNTVOLTBL is laid out $00, $C7, $27, $A7.

 

The code to start the tunes is in the .asm file.

 

$00 and $C7 are for 16-bit mode.  Channel 0 is silenced, and plays the low order bytes of the music (TUNE00) while channel 1 plays the high order (TUNE01).  The DURNxx tables are note durations.  Each note decays as its played.

 

Notes are input using a note table I compiled myself (it's in the 7800 programming forum).  For $Cx track, $0E in the note table is a musical rest (silence).

 

So as a guess, we'd need to find the code which plays the note, check to see if channel 1 is being played (the $Cx channel), and then jsr to the init code if it is, prior to the note being played.

 

The musical code was written by PacManPlus, with some hacking by me to enable 16-bit mode.  This is what I used to do the music for the 7800 game Bentley Bear.

Edited by Synthpopalooza

Share this post


Link to post
Share on other sites

While dealing with 16-bit frequency divisors and C distortion in order to keep synchronization with poly4 counter (keep sampling track) additional requirement must be met. It is atomicity of change of frequency divisor against timer reloading. There are two approaches possible. First one is to guarantee the atomicity by using IRQ on 16-bit channel (2 or 4). The IRQ handler is called right after timer reloading and there is a lot of time to make a change of both bytes of frequency divisor before next timer reloading. The second approach is just to re-synchronize (reset sapling track) on each note change.

Share this post


Link to post
Share on other sites
3 hours ago, Synthpopalooza said:

I can explain some of it.

 

As I said, all you need, for the most basic testing, is to sync the counter before you start playing, and then just make sure you only play desired multiple on the channel you want the bass on. In RMT using C mode no change to tone table is needed, as those note already are multiple of 3. But with your new modes, you will need to change the tone tables, and there is also this atomicity problem Pavros mentions.

It might work without it, but in my experience, if there is few cycles window, sooner or later the counter will flip inside of it, even if it is low frequency. I suggest starting with original Pavros bass, synchronized C, multiple of 3. After it works, go for experiments.

Share this post


Link to post
Share on other sites

The 16-bit table I used here is also in multiples of 3.  The periods are the same too, i.e. $0E = silence.  On the table I linked above you will find it under "Distortion 12b".  I basically tuned it by ear using 16-bit $Ax as a reference point.

 

When I did the same in $4x with 1.79mhz counter, I did find that the period offset changed slightly ... i.e. in 16-bit mode with $4x and 1.79 mhz, $08 produces silence instead of $0E.

 

The other mode i mentioned, $8x with 1.79 and 9-bit poly, is very different.  We are dealing here with frequencies in multiples of 7, and the timbres vary on these too due to timing.

Edited by Synthpopalooza

Share this post


Link to post
Share on other sites

Yes, R0ger... We believe you... but still... the sooner you get all those other projects done, the quicker you can get back to this!

:evil:

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...