ludo icon indicating copy to clipboard operation
ludo copied to clipboard

Refactored audio stack with PortAudio

Open donmor opened this issue 2 years ago • 13 comments

Built with OpenAL, Ludo has audible audio latency (hundreds of milliseconds!), which is unbearable for retro gaming. I managed to refactor it with PortAudio, which is as much portable as OpenAL, and has much lower latency.

donmor avatar Mar 04 '23 10:03 donmor

Awesome, will try it asap...

kivutar avatar Mar 07 '23 07:03 kivutar

Can you replace openal-soft by portaudio in the osx sections of ci.yml and cd.yml?

kivutar avatar Mar 07 '23 07:03 kivutar

I think I know what's wrong with the test on OSX. Not sure why it happens on this PR and not on others though.

Doing this in SetPixelFormat seems to workaround the bug. There is a division by 0 or of 0.

	// PixelStorei also needs to be updated whenever bpp changes
	defer func() {
		if video.pitch != 0 && video.bpp != 0 {
			gl.PixelStorei(gl.UNPACK_ROW_LENGTH, video.pitch/video.bpp)
		}
	}()

Edit: forget what I said, the test did fail on something portaudio related, in portaudio.(*Stream).Close(0x0) in audio.Reconfigure(0xac44) in audio.go:112

kivutar avatar Mar 07 '23 09:03 kivutar

You can just call audio.Init() after Init(&video.Video{}) in Test_coreLoadGame

kivutar avatar Mar 07 '23 10:03 kivutar

BTW is there any package manager other than choco in windows env of github workflow?

donmor avatar Mar 07 '23 11:03 donmor

There may be another one, I see portaudio on a package manager called vcpkg here https://github.com/PortAudio/portaudio/issues/578

I tried your branch on OSX, I noticed 2 things:

  • the audio is crackling a little bit, but noticeably
  • the implementation doesn't use blocking audio, but it's usually good to block on audio in emulation, and portaudio documentation does mention a way of doing it

kivutar avatar Mar 07 '23 15:03 kivutar

I think the crackling occurrs when the core sends data slower than sample rate, causing 0 filled in the interval. Also, since the condition of cores is not expectable, using blocking may cause serious gliches like repeating or unsyncing, I guess.

donmor avatar Mar 08 '23 00:03 donmor

Here' how mine work: write() convert and put samples into paBuf and push paPtr ahead, and blocks the core if it goes too fast, preventing the not-played data in paBuf from being overwrited like in the classic snake game :) paCallback() is called by portaudio, which get samples from paBuf and push paPlayPtr ahead, which always chases paPtr. if paPlayPtr is not smaller than paPtr, the playback stops by receiving zeros.

donmor avatar Mar 08 '23 01:03 donmor

Hmm.. Hey, maybe we can just turn off blocking in write() when it is fast-forwarding, instead of returning size and letting it muted. And volume may be reduced when fast-forwarding.

donmor avatar Mar 08 '23 01:03 donmor

Weird enough... Only right channel is played even though PA stream opened with 2 channels. I'll go to stackoverflow <:-/

donmor avatar Mar 09 '23 13:03 donmor

Okay finally it is stereo now :-)

donmor avatar Mar 10 '23 03:03 donmor

BTW I want to know if the patch will pass ci/cd now :-)

donmor avatar Mar 10 '23 03:03 donmor

It seems that github workflow env has no audio device, causing the failure 'cause portaudio works with audio hardware. We have to do something to the tests <:-/

donmor avatar Mar 13 '23 04:03 donmor