[hatari-devel] Fix for Falcon's noisy sound |
[ Thread Index |
Date Index
| More lists.tuxfamily.org/hatari-devel Archives
]
Hi
After experimenting random noisy sound with the falcon (sometimes it
gives clear sound, sometimes it's noisy, sometimes it goes from clear to
noisy), I spent some time trying to figure what was happening, as random
behaviour with similar starting conditions is often related to a bug,
not necessarily to a lack of precision in cycle counting.
So, in the simple case when playing dmasound using the DAC output, the
DAC uses a 2048 sample ringbuffer. In this buffer, we have readPosition
and writePosition.
If a frame requires 'n' samples to be generated, writePosition will be
incremented 'n' times, then during the during the VBL
Crossbar_GenerateSamples() will be called and read 'n' sample in one go
starting at readPosition.
At start, we have readPosition=writePosition, so everything should be
fine, writePosition will increment by 'n', then readPosition will read
those 'n' new samples.
The problem is when no sample are sent to the DAC : ie, dma sound output
is sent to the DAC, but DMA sound is stopped.
If we print writePosition, readPosition and writePosition-readPosition,
then we can see the problem :
gen 000 34f 4b1
gen 000 738 0c8
gen 000 322 4de
gen 000 70b 0f5
gen 000 2f4 50c
gen 000 6de 122
gen 000 2c7 539
gen 000 6b0 150
gen 000 29a 566
gen 000 683 17d
gen 000 684 17c
gen 033 686 1ad <- DMA sound started here
gen 228 079 1af
gen 41c 26e 1ae
gen 611 463 1ae
gen 006 657 1af
gen 1fa 04c 1ae
gen 3ef 241 1ae
as long as DMA sound is off, only readPosition will change, so read and
write pos are no more in sync. And when DMA is on, readPosition will be
nearly random :(
Depending on the randomness, readPosition can be located in the same
space that writePosition is using to complete the 'n' samples per VBL.
At this point, readPosition will read unfinished sample from the
previous frama, hence the noisy sound.
We see that write-read pos remains constant in this example (0x1ae),
which means that when you hit the noisy sound, it will remain forever in
this case.
But when different freq are used for DMA sound and Hatari output freq,
then there can be some rounding errors and write-read will drift slowly
: you will get correct sound until read and write overlaps on the same
unfinished area and it gives noisy sound. After a while (usually several
dozens of secondes), the drift will set read and write to some more
"compatible" values and sound will become clearer and clearer (until
both position don't overlap at all).
Another problem was that while readPosition incremented, it also zero-ed
previously read sample ; this means that when read and write overlapped,
then the read process would in fact erase parts of the just newly
generated sample :( (this could be seen when recording a wav file of
the sound output : empty block in the wav at fixed space)
This is also why some people reported that changing sound parameters
sometimes fixed the problem : because it gave a new different random
position for writePosition, possibly not overlapping.
The solution to this is to always ensure writePosition is ahead of
readPosition, even when no source is really sending samples to the DAC.
To do this, I added dac.wordCount to count received samples ; if no
sample are received (which usually means the source is off), then we
must correct writePosition.
I set writePosition to one half buffer ahead of readPosition ; this
should not cause any hearable latency and should give some margins in
case we have some drift.
writePosition should be updated everytime dma sound freq changes, or
Hatari sound freq changes or cpu freq changes (as these can trigger some
new rounding errors). Also when restoring a memory snapshot.
Hopefully I handled all the cases to set writePosition, so sound should
remain clear now even if you change preferences or restore a snapshot.
There's still one problem : readPosition and writePosition are not
incremented with the same freq counter in crossbar.c . Although they
both use float for more precision, we can see it's not always correct
and some drift will sometimes appear (depending on the output freq).
For now, I didn't change this, this would require too much rewrite of
crossbar.c logic and it would be too risky with Hatari 2.0 to be
released soon.
There's a second problem with falcon sound : sometimes, there's no DMA
sound at all (I often get this with 030 demo by Doug). This is a
different issue, but the good news is that I found a way to fix it too.
I need to fully check it, but I'm confident this will be OK.
Please, test the changes I pushed to dev version to confirm sound is not
noisy anymore (you should see a 'fix writepos' message each time value
is set)
All in all, I think we should have a much better DMA sound for Falcon in
next Hatari version :)
Nicolas