I have it working, with three tone channels plus a PCM drum channel. This is using the shift register to actually emit the bits, with an unrolled loop generating the data eight bits at a time. It works, kinda...
Using a byte value as the note counter simply isn't enough precision, and I only get about two octaves before the notes become unacceptably out of tune. And everything is pitched very low; C1 to C3. This should have not come as a surprise to anyone other than me as I'm trying to squeeze a log value into eight bits.
So I need to somehow increase the sample rate to get more precision, which means reloading the shift register faster, which means extending the counter to 16 bits, which means doing more work to reload the shift register. I don't think that's going to work.
That brings me back to somehow using a tickless approach where I use the timer to wait precisely for the amount of time needed for the next pulse. Somehow.
Hints on multiplexing multiple timers on a 6522
Re: Hints on multiplexing multiple timers on a 6522
hjalfi wrote:
I have it working, with three tone channels plus a PCM drum channel. This is using the shift register to actually emit the bits, with an unrolled loop generating the data eight bits at a time. It works, kinda...
The Beeb made it much more than just the basics by use of a 100Hz interrupt which could dynamically vary the both frequency and amplitude of the audio output enabling the production of many different effects via a somewhat complex but usable 'envelope' command which took some 14 parameters... Latterly, it was used for speech output by modulating the noise generator too.
So I'm wondering if a simple 16 pin DIP IC could save you a whole lot of effort, although it would not interface to the CPU bus directly, but would need to through a port of a VIA. (The Beeb had what it called a "slow bus" for these sorts of things).
-Gordon
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/
Re: Hints on multiplexing multiple timers on a 6522
@hjalfi it would help if you could share more details. Like which algorithm you actually use now. What timer values / frequencies you are using. What the note counter actually does. Etc.
André
André
Author of the GeckOS multitasking operating system, the usb65 stack, designer of the Micro-PET and many more 6502 content: http://6502.org/users/andre/
Re: Hints on multiplexing multiple timers on a 6522
Sorry for the lack of response --- I managed to get it working well enough, and got stuck in finishing up the project:
https://www.youtube.com/watch?v=iqWJeM2MKt4
This is doing three-channel polyphony with a PCM drum track (well, PFM or PDM, more like).
It works by using the 6522 shift register running at 12529Hz, meaning I have to service it at 1566Hz. There is only just enough CPU time available, and that's with 8-bit counters which aren't really precise enough. The breakthrough was remembering that I only need to synthesise three channels, not four --- the drum track is much easier to generate.
The algorithm is basically:
12529Hz was calculated to give me as much accuracy as possible, so that each note I want to play falls roughly on an integer counter value. In fact it's not great, and I only manage G#0 to C#3 before the inaccuracy goes above 10 cents, which is about the maximum you can get away with. This is a shame, as the demo track really wants to use A3, which is 29 cents flat and sounds terrible. Volume is controlled by changing the mark/space ratio of the output waveforms, but it doesn't work very well. It does produce a nice side-chaining effect, though.
The playback algorithm is using a hair under 100% of the available CPU time, and that's not taking into account the envelope processor (which can apply volume and pitch changes to the playing note) and the time taken to actually calculate what notes to play, and as a result there's an audible glitch between steps in the music, which are mostly covered by effects and drums. Given that I never actually used the envelope processor it's probably not worth having, but maybe someone else will find it useful?
If I had a 2MHz CPU I'd be able to crank the sample rate way up and use 16-bit counters, giving much more accurate music and lower notes.
https://www.youtube.com/watch?v=iqWJeM2MKt4
This is doing three-channel polyphony with a PCM drum track (well, PFM or PDM, more like).
It works by using the 6522 shift register running at 12529Hz, meaning I have to service it at 1566Hz. There is only just enough CPU time available, and that's with 8-bit counters which aren't really precise enough. The breakthrough was remembering that I only need to synthesise three channels, not four --- the drum track is much easier to generate.
The algorithm is basically:
Code: Select all
for every SR interrupt
clear flag
clear var
do eight times
for every voice
decrement note counter
if counter < volume for this voice, set flag
if note counter == 0:
reload it
if flag is set:
rotate a bit into var
OR in the PCM value (which is a byte)
SR := var
The playback algorithm is using a hair under 100% of the available CPU time, and that's not taking into account the envelope processor (which can apply volume and pitch changes to the playing note) and the time taken to actually calculate what notes to play, and as a result there's an audible glitch between steps in the music, which are mostly covered by effects and drums. Given that I never actually used the envelope processor it's probably not worth having, but maybe someone else will find it useful?
If I had a 2MHz CPU I'd be able to crank the sample rate way up and use 16-bit counters, giving much more accurate music and lower notes.
Re: Hints on multiplexing multiple timers on a 6522
Good you managed to get it working!
Some shared code would be nice, we can always learn - on the other hand there's potential optimization we can help with our expertise.
For example, looking at your algorithm, could it not potentially be optimized by exchanging the two loops, and potentially use some arithmatics and tables instead of the inner loop?
How did you test the output? I'm pretty sure the VICE emulator isn't really up to the task - unless they have replaced my pet audio implementation (which is highly likely given that I wrote it some 20+ years ago or so).
Anyway, my suggestion is to test it on a real machine
André
Some shared code would be nice, we can always learn - on the other hand there's potential optimization we can help with our expertise.
For example, looking at your algorithm, could it not potentially be optimized by exchanging the two loops, and potentially use some arithmatics and tables instead of the inner loop?
How did you test the output? I'm pretty sure the VICE emulator isn't really up to the task - unless they have replaced my pet audio implementation (which is highly likely given that I wrote it some 20+ years ago or so).
Anyway, my suggestion is to test it on a real machine
André
Author of the GeckOS multitasking operating system, the usb65 stack, designer of the Micro-PET and many more 6502 content: http://6502.org/users/andre/
Re: Hints on multiplexing multiple timers on a 6522
Oops, I forgot to link to the website: http://cowlark.com/ptracker All the source is on github.
The loops are, in fact, massively unrolled using macros, as a brute-force way to save as many cycles as possible...
(The zif stuff are structured programming macros which turn into simple branch instructions. I find it makes machine code vastly easier to maintain.) (This is from https://github.com/davidgiven/ptracker/ ... ine.S#L140.)
If you can suggest ways to shave cycles off, it desperately needs it.
As for testing... I did actually use VICE. I've been trying to find someone who'll try it on a real PET but nobody's reported back yet!
The loops are, in fact, massively unrolled using macros, as a brute-force way to save as many cycles as possible...
Code: Select all
.macro synth var, varp, varm
ldx \var ; 3
dex ; 2
zif eq ; 2/3
ldx \varp ; 3
zendif
stx \var ; 3
cpx \varm ; 3
adc #0xff ; 2
.endm ; = 18
.rept 8
lda #0
synth t1, t1p, t1m
synth t2, t2p, t2m
synth t3, t3p, t3m
cmp #1 ; sets C if >= 1
rol nextsample
.endr
If you can suggest ways to shave cycles off, it desperately needs it.
As for testing... I did actually use VICE. I've been trying to find someone who'll try it on a real PET but nobody's reported back yet!
Re: Hints on multiplexing multiple timers on a 6522
Still haven't had time to take a deeper look or even test it, but still thinking going on in my head.
Did I get it right that for each note, you define the following two values:
- note counter init value = how many bit periods are in one note period. I.e. at a say 10kHz bit period, a value of 4 would give 2500Hz tone?
- volume per note = how many bits in the note period should be set. So, if a note period is 4, and volume is 4 (you check on less than), there would be 3 one-bits and one zero-bit per note period?
So basically you use a simple PWM to create a volume for a note?
I'm not sure this would work well. The same as all zero and all one bits do not give any tone as the output is constant, I would expect a single one-bit (volume 1) or a single zero-bit (volume 4 in above example) would basically give the same output - your ear does not hear the 180 degree phase shift ... volumne in that definition probably only makes sense up to the half note period.
Definitely on my list to test and play around with
Did I get it right that for each note, you define the following two values:
- note counter init value = how many bit periods are in one note period. I.e. at a say 10kHz bit period, a value of 4 would give 2500Hz tone?
- volume per note = how many bits in the note period should be set. So, if a note period is 4, and volume is 4 (you check on less than), there would be 3 one-bits and one zero-bit per note period?
So basically you use a simple PWM to create a volume for a note?
I'm not sure this would work well. The same as all zero and all one bits do not give any tone as the output is constant, I would expect a single one-bit (volume 1) or a single zero-bit (volume 4 in above example) would basically give the same output - your ear does not hear the 180 degree phase shift ... volumne in that definition probably only makes sense up to the half note period.
Definitely on my list to test and play around with
Author of the GeckOS multitasking operating system, the usb65 stack, designer of the Micro-PET and many more 6502 content: http://6502.org/users/andre/
Re: Hints on multiplexing multiple timers on a 6522
Yup, that's pretty much it. You're right, a high mark/space ratio gives exactly the same effect as a low mark/space ratio. p-tracker only lets you set it from 0 to 15, but on high notes where the period is short odd things happen. In fact the minimum mark time is so high (one 12529Hz sample) that it doesn't have much effect on volume but it does change the timbre significantly. If you listen to the demo track, I'm changing this during long notes to make it sound more interesting.
Re: Hints on multiplexing multiple timers on a 6522
I downloaded the source but as I don't have llvm-mos handy, I wanted to use the binary.
When I started I do have to enter my own sounds?
Is there any demo tune you could share (or if I missed it where I can find it)?
I want to test it on my PET clones...
When I started I do have to enter my own sounds?
Is there any demo tune you could share (or if I missed it where I can find it)?
I want to test it on my PET clones...
Author of the GeckOS multitasking operating system, the usb65 stack, designer of the Micro-PET and many more 6502 content: http://6502.org/users/andre/
Re: Hints on multiplexing multiple timers on a 6522
The disk image has the file ONWARD which is the piece of music in the demo video. If you're not using the disk image, it's in extras/onward-ride.ptr.
Re: Hints on multiplexing multiple timers on a 6522
Bit late to see this, but very interesting..
You might find something useful in a system I wrote some 13 years ago for the C64, synthesiisng mutiple PWM voices at high rates.. EOR buffer, just marking the edges of waveform transitions (1 bit per voice), then generating an essentially run length compressed output of 6526 timer cycles and the resultant summed volume of the bit combination.. Each bit had it's own volume, so a volume lut needed to be generated when voice volumes changes..
Seperate left/right frequencies for Pulse width controls..
Timer A controls the base sample rate (63 cycles in this case), Timer B counts underflows of A, which is basically samples, which makes things nice and 8 bit..
https://csdb.dk/forums/?roomid=12&topicid=90296
You might find something useful in a system I wrote some 13 years ago for the C64, synthesiisng mutiple PWM voices at high rates.. EOR buffer, just marking the edges of waveform transitions (1 bit per voice), then generating an essentially run length compressed output of 6526 timer cycles and the resultant summed volume of the bit combination.. Each bit had it's own volume, so a volume lut needed to be generated when voice volumes changes..
Seperate left/right frequencies for Pulse width controls..
Timer A controls the base sample rate (63 cycles in this case), Timer B counts underflows of A, which is basically samples, which makes things nice and 8 bit..
https://csdb.dk/forums/?roomid=12&topicid=90296