Hi Nicolas,
Whaou, well spotted.
That's a huge change you did here.
And a very interresting analysis.
I'll test it deeply tomorrow, but really great job here.
Best regards
Laurent
Le 06/10/2016 à 00:12, Nicolas Pomarède a écrit :
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