Re: [hatari-devel] Fix for Falcon's noisy sound

[ Thread Index | Date Index | More lists.tuxfamily.org/hatari-devel Archives ]


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






Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/