mackron / miniaudio

Audio playback and capture library written in C, in a single source file.
https://miniaud.io
Other
4.09k stars 362 forks source link

[Resolved] Bad audio cracking with PulseAudio #37

Closed fungos closed 5 years ago

fungos commented 6 years ago

Hi, I'm having a incredibly bad cracking with anything played trough mini_al.

The code adapted from the sample with a few tweaks to chose the backend (copied from another issue here): https://gist.github.com/fungos/7fa6fb159f8fd260ed5aca17b1c0bc5c In this gist there is the link for the input mp3 sample and the output recorded from another device, as if I try to record directly from the pulseaudio sink, the sound is perfect.

I've tried with SDL, OpenAL, PluseAudio, ALSA and OSS backends, all these have the same issue. I don't have any issue at all playing any audio in this desktop with any other software, so I imagine it must be related to some mis-configuration initializing mini_al or some initialization internally.

I want to replace BASS audio in a game port I'm doing, and mini_al sounded ideal for the job, I hope to be able to figure this problem :)

Thanks

mackron commented 6 years ago

Thanks for the report. With that code you posted, you are initializing a mal_context, but you're passing in nullptr as the context argument for mal_device_init(). In this case the device will always be using the default backend and not the one you specified when you initialized the context. To fix this, pass &context as the first argument to mal_device_init(). I think this will help narrow down which backends work and which don't. It's especially surprising that SDL and OpenAL would both exhibit the same crackling behaviour...

Another common cause of crackling is not using a large enough buffer. Try setting one of the following config variables like the code below. Perhaps try setting these to something huge for testing just to eliminate it so we can narrow it down a bit.

config.bufferSizeInMilliseconds = 100;
or
config.bufferSizeInFrames = 4096;

Also, this doesn't affect anything in practice, but this part is unnecessary (line 67):

device.format = decoder.outputFormat;
device.channels = decoder.outputChannels;
device.sampleRate = decoder.outputSampleRate;
mackron commented 6 years ago

I was able to take a listen to your sample files, and that doesn't sound characteristic of an undersized buffer. It actually sounds more like a format conversion error or something... Could you add the following lines to your test program, just before the call the mal_device_start() and report the output?

printf("DECODER: %s / %d / %d\n", mal_get_format_name(decoder.outputFormat), decoder.outputChannels, decoder.outputSampleRate);
printf("DEVICE:  %s / %d / %d / %s\n", mal_get_format_name(device.internalFormat), device.internalChannels, device.internalSampleRate, mal_get_backend_name(device.pContext->backend));

I'm wondering if mini_al is not converting the data between the decoder and the device properly...

Thanks.

fungos commented 6 years ago

The issue is with the PulseAudio backend, the others worked with exception to OSS that failed initialization.

DECODER: 32-bit IEEE Floating Point / 2 / 44100
DEVICE:  16-bit Signed Integer / 2 / 44100 / PulseAudio

I've also tried to tweak the config parameters you suggested with a bunch of different values in both directions and no perceptive change in the output.

Output for each backend differs on device format: SDL and ALSA : DEVICE: 32-bit IEEE Floating Point / 2 / 44100 OpenAL and PulseAudio: DEVICE: 16-bit Signed Integer / 2 / 44100

OpenAL is impacted (slowdown and cracking) by bufferSizeInFrames less than 2Kb. This does not happen with SDL or ALSA. The same for bufferSizeInMilliseconds with OpenAL only.

mackron commented 6 years ago

Thanks a lot for that. That narrows it down a lot. I'm suspecting it's a bug with the f32-to-s16 conversion, but the weird thing is that my automated tests didn't report an error when I tried it the other day. Will investigate.

Yes the OpenAL backend is very high latency and was only intended as an emergency backend for unsupported platforms, but I'll be removing it soonish.

mackron commented 6 years ago

I went ahead and ran your sample program with the same compiler options and the same input file and just to be difficult I've been unable to reproduce the problem... All works fine here. I have, however, pushed an update to the dev branch that adds some debug output, so would you be able to run that program against the PulseAudio backend again and post the output? It'll spam a bunch of messages originating from the callback. I only need the first bit of the output and just a small sample of the looped output from the callback.

By the way, mini_al explicitly disables OSS on Linux. It's only enabled for FreeBSD and DragonFly BSD.

fungos commented 6 years ago

Here my output:

[mini_al] Endian:  LE
[mini_al] SSE2:    YES
[mini_al] AVX2:    NO
[mini_al] AVX512F: NO
[mini_al] NEON:    NO
[PulseAudio] attr: maxlength=123200, tlength=123200, prebuf=-1, minreq=61600, fragsize=61600; bufferSizeInFrames=30800
[PulseAudio] actual attr: maxlength=123200, tlength=123200, prebuf=61604, minreq=61600, fragsize=61600; pDevice->bufferSizeInFrames=30800
[PulseAudio] Audio interne Stéréo analogique (Playback)
  Format:      32-bit IEEE Floating Point -> 16-bit Signed Integer
  Channels:    2 -> 2
  Sample Rate: 44100 -> 44100
Using backend: PulseAudio
DECODER: 32-bit IEEE Floating Point / 2 / 44100
DEVICE:  16-bit Signed Integer / 2 / 44100 / PulseAudio
[PulseAudio] write_callback: sizeInBytes=123200
    bytesToReadFromClient=65472, framesToReadFromClient=16368
    bytesToReadFromClient=57728, framesToReadFromClient=14432
[PulseAudio] write_callback: sizeInBytes=61636
    bytesToReadFromClient=61636, framesToReadFromClient=15409
[PulseAudio] write_callback: sizeInBytes=61600
    bytesToReadFromClient=61600, framesToReadFromClient=15400
[PulseAudio] write_callback: sizeInBytes=61600
    bytesToReadFromClient=61600, framesToReadFromClient=15400
[PulseAudio] write_callback: sizeInBytes=61600
    bytesToReadFromClient=61600, framesToReadFromClient=15400
...
mackron commented 6 years ago

To anyone whose listening, has anybody else out there experienced this issue? This is so frustrating because that all looks normal. In fact it looks identical to my test, except with different buffer sizes (yours still look fine, though).

I made a few code changes yesterday - I assume the audio output sounded exactly the same with no improvements?

mackron commented 6 years ago

I expect it won't make any difference, but could you try compiling with #define MAL_NO_SSE2 and see how that goes?

fungos commented 6 years ago

No difference at all with MAL_NO_SSE2.

EDIT: Sorry, I had another change with it.

mackron commented 6 years ago

So frustrating! If it's not a hassle, would you be able to capture the output like you did the first time, only with MAL_NO_SSE2? I just want to see if it sounds characteristic of certain types of errors. I have no idea what would cause this... The fact that it sounds different with MAL_NO_SSE2 means it must be something with mini_al, but what???

fungos commented 6 years ago

There is it: https://mega.nz/#!8MVhUY5Q!WCGd7SiBAslsHMnwMoAY-DMIEsAqgD0-H6WBWG3V6aI I can't hear any difference. Watch out your volume.

mackron commented 6 years ago

I've pushed some more debug output updates to the dev branch. Could you give that a quick run and post the output?

Mine looks like the following: Pretty much identical:

[mini_al] Endian:  LE
[mini_al] SSE2:    NO
[mini_al] AVX2:    NO
[mini_al] AVX512F: NO
[mini_al] NEON:    NO
[PulseAudio] attr: maxlength=4400, tlength=4400, prebuf=-1, minreq=2200, fragsize=2200; bufferSizeInFrames=1100
[PulseAudio] actual attr: maxlength=4400, tlength=4400, prebuf=2204, minreq=2200, fragsize=2200; pDevice->bufferSizeInFrames=1100
[PulseAudio] Built-in Audio Analogue Stereo (Playback)
  Format:      32-bit IEEE Floating Point -> 16-bit Signed Integer
  Channels:    2 -> 2
  Sample Rate: 44100 -> 44100
  Conversion:
    Pre Format Conversion:    NO
    Post Format Conversion:   YES
    Channel Routing:          NO
    SRC:                      NO
    Channel Routing at Start: NO
    Passthrough:              NO
Using backend: PulseAudio
DECODER: 32-bit IEEE Floating Point / 2 / 44100
DEVICE:  16-bit Signed Integer / 2 / 44100 / PulseAudio
[PulseAudio] write_callback: sizeInBytes=4400
    bytesToReadFromClient=4400, framesToReadFromClient=1100
[PulseAudio] write_callback: sizeInBytes=4400
    bytesToReadFromClient=4400, framesToReadFromClient=1100
[PulseAudio] write_callback: sizeInBytes=4400
    bytesToReadFromClient=4400, framesToReadFromClient=1100
Press Enter to quit...[PulseAudio] write_callback: sizeInBytes=4400
    bytesToReadFromClient=4400, framesToReadFromClient=1100
[PulseAudio] write_callback: sizeInBytes=4400
    bytesToReadFromClient=4400, framesToReadFromClient=1100
[PulseAudio] write_callback: sizeInBytes=3872
    bytesToReadFromClient=3872, framesToReadFromClient=968
[PulseAudio] write_callback: sizeInBytes=4400
    bytesToReadFromClient=4400, framesToReadFromClient=1100
fungos commented 6 years ago
[mini_al] Endian:  LE
[mini_al] SSE2:    NO
[mini_al] AVX2:    NO
[mini_al] AVX512F: NO
[mini_al] NEON:    NO
[PulseAudio] attr: maxlength=4800, tlength=4800, prebuf=-1, minreq=2400, fragsize=2400; bufferSizeInFrames=1200
[PulseAudio] actual attr: maxlength=4800, tlength=4800, prebuf=2404, minreq=2400, fragsize=2400; pDevice->bufferSizeInFrames=1200
[PulseAudio] Audio interne Stéréo analogique (Playback)
  Format:      32-bit IEEE Floating Point -> 16-bit Signed Integer
  Channels:    2 -> 2
  Sample Rate: 44100 -> 48000
  Conversion:
    Pre Format Conversion:    YES
    Post Format Conversion:   YES
    Channel Routing:          NO
    SRC:                      YES
    Channel Routing at Start: NO
    Passthrough:              NO
Using backend: PulseAudio
DECODER: 32-bit IEEE Floating Point / 2 / 44100
DEVICE:  16-bit Signed Integer / 2 / 48000 / PulseAudio
[PulseAudio] write_callback: sizeInBytes=4800
    bytesToReadFromClient=4800, framesToReadFromClient=1200
[PulseAudio] write_callback: sizeInBytes=2408
    bytesToReadFromClient=2408, framesToReadFromClient=602
[PulseAudio] write_callback: sizeInBytes=2424
    bytesToReadFromClient=2424, framesToReadFromClient=606

I've just noticed it changed my output sample rate to 48000. I'm pretty sure I didn't change anything.

mackron commented 6 years ago

Yeah it's the same as mine (except for how your PulseAudio server changed sample rates between runs?). Does it work on a different device by the way (it's no problem if you don't have another device to test on - just trying to think about different possibilities)?

fungos commented 6 years ago

It works on my wireless headset! wt..? This is really weird and annoying, why this would make any difference?

[mini_al] Endian:  LE
[mini_al] SSE2:    NO
[mini_al] AVX2:    NO
[mini_al] AVX512F: NO
[mini_al] NEON:    NO
[PulseAudio] attr: maxlength=4400, tlength=4400, prebuf=-1, minreq=2200, fragsize=2200; bufferSizeInFrames=1100
[PulseAudio] actual attr: maxlength=4400, tlength=4400, prebuf=2204, minreq=2200, fragsize=2200; pDevice->bufferSizeInFrames=1100
[PulseAudio] Wireless Stereo Headset Stéréo analogique (Playback)
  Format:      32-bit IEEE Floating Point -> 16-bit Signed Integer
  Channels:    2 -> 2
  Sample Rate: 44100 -> 44100
  Conversion:
    Pre Format Conversion:    NO
    Post Format Conversion:   YES
    Channel Routing:          NO
    SRC:                      NO
    Channel Routing at Start: NO
    Passthrough:              NO
Using backend: PulseAudio
DECODER: 32-bit IEEE Floating Point / 2 / 44100
DEVICE:  16-bit Signed Integer / 2 / 44100 / PulseAudio
[PulseAudio] write_callback: sizeInBytes=4400
    bytesToReadFromClient=4400, framesToReadFromClient=1100
[PulseAudio] write_callback: sizeInBytes=2216
    bytesToReadFromClient=2216, framesToReadFromClient=554
[PulseAudio] write_callback: sizeInBytes=3172
    bytesToReadFromClient=3172, framesToReadFromClient=793

Tested with my output in both devices simultaneous and no cracking at all. But if I change to the internal analogic, there it is again.

$ lspci
01:00.1 Audio device: NVIDIA Corporation GK104 HDMI Audio Controller (rev a1)

$ lsmod | grep snd
snd_usb_audio         155648  2
snd_usbmidi_lib        28672  1 snd_usb_audio
snd_rawmidi            28672  1 snd_usbmidi_lib
snd_seq_device         16384  1 snd_rawmidi
snd_hda_codec_hdmi     49152  1
snd_hda_codec_realtek    73728  1
snd_hda_codec_generic    69632  1 snd_hda_codec_realtek
snd_hda_intel          36864  4
snd_hda_codec         106496  4 snd_hda_intel,snd_hda_codec_hdmi,snd_hda_codec_generic,snd_hda_codec_realtek
snd_hda_core           65536  5 snd_hda_intel,snd_hda_codec,snd_hda_codec_hdmi,snd_hda_codec_generic,snd_hda_codec_realtek
snd_hwdep              16384  2 snd_hda_codec,snd_usb_audio
snd_pcm                90112  5 snd_hda_intel,snd_hda_codec,snd_usb_audio,snd_hda_core,snd_hda_codec_hdmi
snd_timer              28672  1 snd_pcm
snd                    77824  24 snd_hda_intel,snd_hwdep,snd_hda_codec,snd_usb_audio,snd_timer,snd_rawmidi,snd_hda_codec_hdmi,snd_hda_codec_generic,snd_usbmidi_lib,snd_seq_device,snd_hda_codec_realtek,snd_pcm
soundcore              16384  1 snd
usbcore               212992  9 usbhid,snd_usb_audio,usb_storage,ehci_hcd,ohci_pci,snd_usbmidi_lib,uas,ohci_hcd,ehci_pci

$ aplay --list-devices
**** List of PLAYBACK Hardware Devices ****
card 0: SB [HDA ATI SB], device 0: ALC887-VD Analog [ALC887-VD Analog]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: SB [HDA ATI SB], device 1: ALC887-VD Digital [ALC887-VD Digital]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: NVidia [HDA NVidia], device 3: HDMI 0 [HDMI 0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: NVidia [HDA NVidia], device 7: HDMI 1 [HDMI 1]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: NVidia [HDA NVidia], device 8: HDMI 2 [HDMI 2]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: NVidia [HDA NVidia], device 9: HDMI 3 [HDMI 3]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 2: Headset [Wireless Stereo Headset], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
mackron commented 6 years ago

You know what... I think I've experienced this issue before... If it's not a hassle for you, could you try this fix from the Arch wiki: https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling

The "tsched=0" thing is something that I could have sworn I've done in the past which fixed a problem... Also look at the "Static noise when using headphones" section.

fungos commented 6 years ago

You got it, this fixes the issue! It is weird the explanation "Timer-based scheduling may expose issues in some ALSA drivers" as mini_al using ALSA backed worked perfectly.

I would suggest adding this link somewhere in the source near the pulseaudio backend and maybe try to identify the issue in someway and give a warning. I noted that Pre Format Conversion and SRC were YES before, and now they are NO. That may be useful as a way to try detect the issue when using PulseAudio.

mackron commented 6 years ago

The thing is, this is something I experienced over a year ago (hence why I didn't initially think of it) and it's still not fixed... I'm just wondering what mini_al is doing differently to other software. Why is other software working, and what are they doing differently?

Anyway I'm glad that's working for you now! I'm going to leave this open for the time being to remind me to look into this further and see if I can work around it because this is kind of a serious problem. In the meantime I've added a note to the documentation at the top of mini_al in case this happens again.

Thanks for going through all the hassle of testing that stuff!

Paspartout commented 5 years ago

I think I am experiencing the same bug on Ubuntu 18.04. The workaround from the ArchWiki works for me too. If you need any data/logs, let me know.

mackron commented 5 years ago

Thanks for the report. I'd be interested to know what hardware you're using, but otherwise I'm not sure what I can do about this one. I'll need to look at how other libraries are using Pulse for some ideas I think.

mackron commented 5 years ago

The dev_0.9 branch has a refactor of the PulseAudio backend which I'm hoping may fix this. I'm not 100% sure if it's entirely fixed, but definitely I'm not reproducing it on a fresh Antergos install (based on Arch). I'm going to go ahead and close this issue, but I'm leaving the comment in the code file.