Page 2 of 3
Posted: Wed Sep 29, 2010 12:43 pm
by neilbaldwin
Damn! I think he's got it.
There's still an elephant in the room though.
Posted: Wed Sep 29, 2010 1:26 pm
by blargg
You mean why the waveform in your screenshot appears to jump back a few phase steps, rather than forward by a few as one would expect? No idea on that. Or perhaps why it occurs at different rates on different emulators? Granularity of them running their emulated APU, or possibly different initial timing or timing accuracy of the emulator in general. Occurrence would be a complex combination of things so that slight changes in accuracy would have a big effect on the pattern of occurrence (e.g. it's chaotic).
Posted: Wed Sep 29, 2010 1:49 pm
by neilbaldwin
blargg wrote:You mean why the waveform in your screenshot appears to jump back a few phase steps, rather than forward by a few as one would expect? No idea on that. Or perhaps why it occurs at different rates on different emulators? Granularity of them running their emulated APU, or possibly different initial timing or timing accuracy of the emulator in general. Occurrence would be a complex combination of things so that slight changes in accuracy would have a big effect on the pattern of occurrence (e.g. it's chaotic).
Well yeah, that and how is it possible to write two registers simultaneously?

Posted: Wed Sep 29, 2010 1:56 pm
by Bregalad
It's impossible but I see two potential fixes :
- Write a dummy nonzero value to $400b (such as #$07), the real $400a value and the real $400b value
- Write in the order $400a, $400b when the period goes upward, and the order $400b, $400a when it goes downward. Sounds a bit more difficult to implement, as you'd have to compare new and old values or something in the like.
Posted: Wed Sep 29, 2010 2:29 pm
by blargg
Nice. It might be better to write $80 to $400A, to avoid any length counter/linear counter issues (better to avoid messing with it if you can). So something like this as the simplest-but-effective approach:
lda #$FF
sta $400A
lda high
sta $400B
lda low
sta $400A
Posted: Wed Sep 29, 2010 3:44 pm
by neilbaldwin
Writing dummy values doesn't seem to improve it at all, if anything it's worse.
However I tried the switching-the-order-depending-on-direction idea like this;
Code: Select all
triVib: ldx toneIndex
lda toneHi,x
sec
sbc oldHi
beq @c
bmi @down
lda toneHi,x
sta $400B
sta oldHi
@c: lda toneLo,x
cmp oldLo
beq @next
sta $400a
sta oldLo
jmp @next
@down lda toneLo,x
cmp oldLo
beq @d
sta $400a
sta oldLo
@d: lda toneHi,x
sta $400b
sta oldHi
@next inx
cpx #$0C
bcc @b
ldx #$00
@b: stx toneIndex
@a: rts
and it's much better though you do still get the occasional click.
Posted: Wed Sep 29, 2010 5:56 pm
by clueless
Would it be worth caching the two values to be written into A and Y, and then writing them back-to-back. You'll still have a race condition, but it will be down to 3 clock cycles instead of however many you currently have (8?).
ie..
lda something, X
tay
lda other, X
sta APU_a
sty APU_b
Posted: Thu Sep 30, 2010 1:18 am
by neilbaldwin
Well I guess the test program I did isn't the best implementation of how to write the two period registers.
In my actual audio engine it's just a pair of lda/sta instructions;
lda periodLo
sta $400a
lda periodHi
sta $400b
which could easily be changed to;
lda periodLo
ldx periodHi
sta $400a
stx $400b
but given blargg's (highly plausible) theory on why the click happens, it could still happen in between the two writes i.e. 3 cycles is not 0 cycles
The other thing I tried (which failed) was to always save a copy of what is written to the hi period and then when writing the lo period, ORA that with the saved hi period and if it comes out zero, do a ORA #$01 before writing the lo period register. I was trying to engineer it so that $00/$00 would never exist in the registers but it didn't seem to work. Possibly flawed logic and also not a great solution from a musical point of view since it's slightly detuning the period.
Posted: Thu Sep 30, 2010 7:55 am
by Dwedit
Write the max possible value to period high, then write period low, then write period high?
If you use X Y and A, you can do all writes within 4 clock cycles of each other.
Posted: Thu Sep 30, 2010 8:56 am
by Bregalad
OK it might be stupid for me to ask it this way but did you try both techniques I mentioned ? At least of them should somewhat work I guess
At worse, I think the frequency for a A note on a particular octave is $fd. Maybe you could fine-tune everything a bit up, so that it is something around $fb or $fa. Then you can get vibratoes of a great depth without ever touching the infamous $ff/$100 barrier. Then it would fix all triangle and square channel problems on vibratoes (except those of a really big depth) - but it won't fix pitch slides (which is less noticeable as the click happens only once). Lazy, but might work surprisingly well.

Posted: Thu Sep 30, 2010 9:06 am
by neilbaldwin
Bregalad wrote:OK it might be stupid for me to ask it this way but did you try both techniques I mentioned ? At least of them should somewhat work I guess
At worse, I think the frequency for a A note on a particular octave is $fd. Maybe you could fine-tune everything a bit up, so that it is something around $fb or $fa. Then you can get vibratoes of a great depth without ever touching the infamous $ff/$100 barrier. Then it would fix all triangle and square channel problems on vibratoes (except those of a really big depth) - but it won't fix pitch slides (which is less noticeable as the click happens only once). Lazy, but might work surprisingly well.

Yep, both methods. Don't take my word for it though - the code I'm using is there in the post and is compilable
Just to be clear - it actually only happens on the triangle voice. I think it's because only the triangle voice has it's output silenced when the period is 00/00?
Out of everything I've tried the changing-write-order-depending-on-direction method of yours seems to yield the best (but not 100%) results so I think I'm going to go with that.
At least until blargg comes up with a solution

Posted: Thu Sep 30, 2010 9:07 am
by blargg
The other thing I tried (which failed) was to always save a copy of what is written to the hi period and then when writing the lo period, ORA that with the saved hi period and if it comes out zero, do a ORA #$01 before writing the lo period register.
Remember, even a period of $0001 is too low. That causes it to clock the phase every other clock, so you can still get a slight click.
Dwedit wrote:Write the max possible value to period high, then write period low, then write period high?
Then you might get a little rest in the output. As long as the period never goes below 128 or so, you won't get quick clocking. And again, I recommend against writing to $400B any more than absolutely necessary.
clueless wrote:lda something, X
tay
lda other, X
sta APU_a
sty APU_b
BTW, you can read directly into Y. Here are some addressing modes often overlooked:
ldy abs,x
ldx abs,y
sty zp,x
stx zp,y
Posted: Thu Sep 30, 2010 9:25 am
by Bregalad
OK maybe my second method didn't work because I explained it backwards (I can't say how much hate when you have 1/2 chances of saying something wrong - I always end up saying the wrong thing no matter how much I think about it).
When the pitch is decreasing, and the period increasing, you're doing $ff -> $100 you want to write first to $400b ($00 -> $01) and then to $400a ($ff -> $00) so the period is never $0000.
When the pitch is increasing, and the period increasing, you're doing $100 -> $ff, you want to write first to $400a ($00 -> $ff) and then to $400b ($01 -> $00), so again the period is never $0000.
This should always work.
Also, I don't see the problem of the dummy value write. Sure the period will be wrong for a few cycles, but not long enough to cause (especially if the high dummy value is $07, which makes the counter less likely to be clocked- and if clocked it's only once so not an issue).
Just to be clear - it actually only happens on the triangle voice.
I know but other problem arises when changing the high period of square voices, the phase is reset. I like to bring up lazy solutions to problems, because those solutions would have been more likely seen implemented in games in the 80's than crazy solutions which relies on obscure tricks on the hardware and that few if any emulator supports (blarrg's sweep counter abuse method).
Posted: Thu Sep 30, 2010 11:31 pm
by neilbaldwin
Bregalad wrote:OK maybe my second method didn't work because I explained it backwards (I can't say how much hate when you have 1/2 chances of saying something wrong - I always end up saying the wrong thing no matter how much I think about it).
When the pitch is decreasing, and the period increasing, you're doing $ff -> $100 you want to write first to $400b ($00 -> $01) and then to $400a ($ff -> $00) so the period is never $0000.
When the pitch is increasing, and the period increasing, you're doing $100 -> $ff, you want to write first to $400a ($00 -> $ff) and then to $400b ($01 -> $00), so again the period is never $0000.
Yep, this is exactly what I did, see the last bit of code I posted. It does work but not 100%, for some reason.
Bregalad wrote:This should always work.
Also, I don't see the problem of the dummy value write. Sure the period will be wrong for a few cycles, but not long enough to cause (especially if the high dummy value is $07, which makes the counter less likely to be clocked- and if clocked it's only once so not an issue).
It just didn't have any positive effect. The clicking was still there, perhaps even more prominent (but that's debatable).
Bregalad wrote:
I know but other problem arises when changing the high period of square voices, the phase is reset. I like to bring up lazy solutions to problems, because those solutions would have been more likely seen implemented in games in the 80's than crazy solutions which relies on obscure tricks on the hardware and that few if any emulator supports (blarrg's sweep counter abuse method).
The square channel click is a different issue and is overcome in both NTRQ and Pulsar using blargg's clever trick with the sweep registers.
Posted: Sat Nov 06, 2010 11:33 am
by B00daW
Call me nuts, but can you replicate the "click" in succession to create a low-volume saw wave?