Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pulse Audio Exception - Hard to Reproduce #952

Open
DavidEBrumbaugh opened this issue Sep 12, 2023 · 17 comments
Open

Pulse Audio Exception - Hard to Reproduce #952

DavidEBrumbaugh opened this issue Sep 12, 2023 · 17 comments
Labels
bug Something isn't working

Comments

@DavidEBrumbaugh
Copy link

Describe the bug
I'm using Python Arcade 3.0.0.dev25 and Pyglet 2.0.0.9. Randomly (usually when I pause a lot when debugging, but also when I've been running the game for a while) I get the following exception thrown:
Assertion 'q->front' failed at pulsecore/queue.c:81, function pa_queue_push(). Aborting.

The bug is clearly in pulse audio. It was listed as a "closed issue": mackron/miniaudio#235
And fixed here:
mackron/miniaudio@50c9081

System Information:

pyglet.version: 2.0.9
pyglet.compat_platform: linux
pyglet.__file__: /home/david/.virtualenvs/venv_arcade/lib/python3.10/site-packages/pyglet/__init__.py
pyglet.options['audio'] = ('xaudio2', 'directsound', 'openal', 'pulse', 'silent')
pyglet.options['debug_font'] = False
pyglet.options['debug_gl'] = True
pyglet.options['debug_gl_trace'] = False
pyglet.options['debug_gl_trace_args'] = False
pyglet.options['debug_gl_shaders'] = False
pyglet.options['debug_graphics_batch'] = False
pyglet.options['debug_lib'] = False
pyglet.options['debug_media'] = False
pyglet.options['debug_texture'] = False
pyglet.options['debug_trace'] = False
pyglet.options['debug_trace_args'] = False
pyglet.options['debug_trace_depth'] = 1
pyglet.options['debug_trace_flush'] = True
pyglet.options['debug_win32'] = False
pyglet.options['debug_input'] = False
pyglet.options['debug_x11'] = False
pyglet.options['shadow_window'] = True
pyglet.options['vsync'] = None
pyglet.options['xsync'] = True
pyglet.options['xlib_fullscreen_override_redirect'] = False
pyglet.options['search_local_libs'] = True
pyglet.options['win32_gdi_font'] = False
pyglet.options['headless'] = False
pyglet.options['headless_device'] = 0
pyglet.options['win32_disable_shaping'] = False
pyglet.options['dw_legacy_naming'] = False
pyglet.options['win32_disable_xinput'] = False
pyglet.options['com_mta'] = False
pyglet.options['osx_alt_loop'] = False

pyglet.window
------------------------------------------------------------------------------
display: <pyglet.canvas.xlib.XlibDisplay object at 0x7f8a319e2d10>
screens[0]: XlibScreen(display=<pyglet.canvas.xlib.XlibDisplay object at 0x7f8a319e2d10>, x=0, y=0, width=1920, height=1080, xinerama=False)
config['double_buffer'] = 1
config['stereo'] = 0
config['buffer_size'] = 24
config['aux_buffers'] = 0
config['sample_buffers'] = 0
config['samples'] = 0
config['red_size'] = 8
config['green_size'] = 8
config['blue_size'] = 8
config['alpha_size'] = 0
config['depth_size'] = 24
config['stencil_size'] = 0
config['accum_red_size'] = 0
config['accum_green_size'] = 0
config['accum_blue_size'] = 0
config['accum_alpha_size'] = 0
config['major_version'] = 3
config['minor_version'] = 3
config['forward_compatible'] = None
config['opengl_api'] = 'gl'
config['debug'] = None
context: XlibContext(id=140231516124640, share=XlibContext(id=140231514667616, share=None))

window.context._info
------------------------------------------------------------------------------
gl_info.get_version(): (4, 6)
gl_info.get_vendor(): Intel
gl_info.get_renderer(): Mesa Intel(R) UHD Graphics 630 (CFL GT2)

pyglet.gl.glx_info
------------------------------------------------------------------------------
context.is_direct(): 1
glx_info.get_server_vendor(): SGI
glx_info.get_server_version(): 1.4
glx_info.get_server_extensions():
   GLX_ARB_create_context
   GLX_ARB_create_context_no_error
   GLX_ARB_create_context_profile
   GLX_ARB_create_context_robustness
   GLX_ARB_fbconfig_float
   GLX_ARB_framebuffer_sRGB
   GLX_ARB_multisample
   GLX_EXT_create_context_es_profile
   GLX_EXT_create_context_es2_profile
   GLX_EXT_fbconfig_packed_float
   GLX_EXT_framebuffer_sRGB
   GLX_EXT_import_context
   GLX_EXT_libglvnd
   GLX_EXT_no_config_context
   GLX_EXT_texture_from_pixmap
   GLX_EXT_visual_info
   GLX_EXT_visual_rating
   GLX_MESA_copy_sub_buffer
   GLX_OML_swap_method
   GLX_SGI_make_current_read
   GLX_SGI_swap_control
   GLX_SGIS_multisample
   GLX_SGIX_fbconfig
   GLX_SGIX_pbuffer
   GLX_SGIX_visual_select_group
   GLX_INTEL_swap_event
glx_info.get_client_vendor(): Mesa Project and SGI
glx_info.get_client_version(): 1.4
glx_info.get_client_extensions():
   GLX_ARB_context_flush_control
   GLX_ARB_create_context
   GLX_ARB_create_context_no_error
   GLX_ARB_create_context_profile
   GLX_ARB_create_context_robustness
   GLX_ARB_fbconfig_float
   GLX_ARB_framebuffer_sRGB
   GLX_ARB_get_proc_address
   GLX_ARB_multisample
   GLX_EXT_buffer_age
   GLX_EXT_create_context_es2_profile
   GLX_EXT_create_context_es_profile
   GLX_EXT_fbconfig_packed_float
   GLX_EXT_framebuffer_sRGB
   GLX_EXT_import_context
   GLX_EXT_no_config_context
   GLX_EXT_swap_control
   GLX_EXT_swap_control_tear
   GLX_EXT_texture_from_pixmap
   GLX_EXT_visual_info
   GLX_EXT_visual_rating
   GLX_ATI_pixel_format_float
   GLX_INTEL_swap_event
   GLX_MESA_copy_sub_buffer
   GLX_MESA_multithread_makecurrent
   GLX_MESA_query_renderer
   GLX_MESA_swap_control
   GLX_NV_float_buffer
   GLX_OML_swap_method
   GLX_OML_sync_control
   GLX_SGIS_multisample
   GLX_SGIX_fbconfig
   GLX_SGIX_pbuffer
   GLX_SGIX_visual_select_group
   GLX_SGI_make_current_read
   GLX_SGI_swap_control
   GLX_SGI_video_sync
glx_info.get_extensions():
   GLX_ARB_create_context
   GLX_ARB_create_context_no_error
   GLX_ARB_create_context_profile
   GLX_ARB_create_context_robustness
   GLX_ARB_fbconfig_float
   GLX_ARB_framebuffer_sRGB
   GLX_ARB_get_proc_address
   GLX_ARB_multisample
   GLX_EXT_buffer_age
   GLX_EXT_create_context_es2_profile
   GLX_EXT_create_context_es_profile
   GLX_EXT_fbconfig_packed_float
   GLX_EXT_framebuffer_sRGB
   GLX_EXT_import_context
   GLX_EXT_no_config_context
   GLX_EXT_swap_control
   GLX_EXT_swap_control_tear
   GLX_EXT_texture_from_pixmap
   GLX_EXT_visual_info
   GLX_EXT_visual_rating
   GLX_INTEL_swap_event
   GLX_MESA_copy_sub_buffer
   GLX_MESA_query_renderer
   GLX_MESA_swap_control
   GLX_OML_swap_method
   GLX_OML_sync_control
   GLX_SGIS_multisample
   GLX_SGIX_fbconfig
   GLX_SGIX_pbuffer
   GLX_SGIX_visual_select_group
   GLX_SGI_make_current_read
   GLX_SGI_swap_control
   GLX_SGI_video_sync

pyglet.media
------------------------------------------------------------------------------
audio driver: <pyglet.media.drivers.pulse.adaptation.PulseAudioDriver object at 0x7f8a319e3eb0>

pyglet.media.ffmpeg
------------------------------------------------------------------------------
FFmpeg version: 4.2.7-0ubuntu0.1

pyglet.media.drivers.openal
------------------------------------------------------------------------------
OpenAL not available.

pyglet.input.wintab
------------------------------------------------------------------------------
WinTab not available.

How To Reproduce
This is the code I've written to start the sound most of the time. When a sprite triggers an action (i.e. starts walking) if the player sprite is close enough, the sound turns on. This code snippet is typical of when I load or turn on sound. Typically there are two sounds playing when the exception happens.

def start(self, player_character: arcade.Sprite, source_sprite: arcade.Sprite, eos=None):
        self.player_character = player_character
        self.sound_source = source_sprite
        self.eos = eos
        self.sound = arcade.Sound(self.file_name, streaming=False)
        if self.min_range <= 0:  # Start playing right away
            self.media_player = self.sound.play(volume=self.max_volume, loop=self.loop)
            # Todo: Add EOS logic
@DavidEBrumbaugh DavidEBrumbaugh added the bug Something isn't working label Sep 12, 2023
@DavidEBrumbaugh
Copy link
Author

After looking at the system info, I realized I didn't have OpenAL installed - so I did. Obviously, this is a work around, but I did notice everything sounded better (the distance from player seemed to work better).

@pushfoo
Copy link
Contributor

pushfoo commented Sep 12, 2023

The bug is clearly in pulse audio. It was listed as a "closed issue":

Could you please you post the following?

  1. The version of AVbin pyglet is using
  2. The miniaudio version that AVbin is using
  3. Your Ubuntu version

Although the issue you linked was closed in 2020, the fix might not be shipped on older LTS versions.

@DavidEBrumbaugh
Copy link
Author

No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.6 LTS
Release:	20.04
Codename:	focal

Please forgive my ignorance on AVbin ... can you tell me how to check ?

@pushfoo
Copy link
Contributor

pushfoo commented Sep 13, 2023

My apologies, I think there was a misunderstanding:

  1. The current version of pyglet.media.drivers seem to bind PulseAudio and OpenAL directly via ctypes instead of using miniaudio or AVbin
  2. Based on the miniaudio issue, PR, and commit, it looks like it's race condition from how access is handed off rather than a bug in PulseAudio itself

The contradictory docstring at the top of the PulseAudio binding is also concerning. Combined with the issues you linked, it's probably good starting point for investigating this further:

"""Wrapper for pulse
Generated with:
tools/genwrappers.py pulseaudio
Do not modify this file.
IMPORTANT: struct_timeval is incorrectly parsed by tools/genwrappers.py and
was manually edited in this file.
"""

I'm not familiar with audio and am unsure if I'll be able to fix this.

Obviously, this is a work around

A workaround may be better than nothing, especially if the required packages are widely available. Documenting them and the setup steps would be a decent short-term solution.

Could you please re-run the pyglet system info command and confirm the following?

  1. You no longer get the exception when using OpenAL
  2. The pyglet.media.drivers.openal section no longer reads OpenAL not available.
  3. The pyglet.media section's audio driver entry now starts with variation of audio driver: <pyglet.media.drivers.openal.adaptation.OpenALDriver

@pushfoo
Copy link
Contributor

pushfoo commented Sep 13, 2023

Randomly (usually when I pause a lot when debugging, but also when I've been running the game for a while) I get the following exception thrown:

On further thought, this behavior seems understandable in the context of debugging and other reports of the same error. It may be an underflow caused by debugging or loss of sync:

  1. There's a get_timing_info method that the OOP abstraction uses

def get_timing_info(self):
context = self.context()
assert context is not None
assert self._pa_stream is not None
timing_info = context.check_ptr_not_null(
pa.pa_stream_get_timing_info(self._pa_stream)
)
return timing_info.contents

2. Debugging can do odd things to programs that aren't built to account for odd jumps in time
3. On resuming or loss of sync, pyglet's abstraction could try to fetch nonexistent data due to the time data being stale

I think you've also revealed why I've never run into this issue despite contributing to arcade for years on Linux: I've been using OpenAL the whole time.

pyglet.media
------------------------------------------------------------------------------
audio driver: <pyglet.media.drivers.openal.adaptation.OpenALDriver object at 0x7f8929cdb430>

Interestingly, this user reported that installing PipeWire with a PulseAudio adapter layer also fixed the problem. To me, this reads as one of the following:

  1. PulseAudio is indeed buggy
  2. The design of PulseAudio may be flawed since this issue comes up surprisingly frequently in search results

I could try to install or switch to a PulseAudio adapter layer to replicate this issue, but it might be pointless.

@DavidEBrumbaugh
Copy link
Author

  1. I have not had the exception since I installed OpenAL, it was happening once or twice a day before and has not happened since.
  2. Confirmed
pyglet.media.drivers.openal
------------------------------------------------------------------------------
Library: <CDLL 'libopenal.so.1', handle 3359e30 at 0x7f7bf4159960>
Version: 1.1
Extensions:
   ALC_ENUMERATE_ALL_EXT
   ALC_ENUMERATION_EXT
   ALC_EXT_CAPTURE
   ALC_EXT_DEDICATED
   ALC_EXT_disconnect
   ALC_EXT_EFX
   ALC_EXT_thread_local_context
   ALC_SOFT_device_clock
   ALC_SOFT_HRTF
   ALC_SOFT_loopback
   ALC_SOFT_output_limiter
   ALC_SOFT_pause_device

  1. Confirmed
pyglet.media
------------------------------------------------------------------------------
audio driver: <pyglet.media.drivers.openal.adaptation.OpenALDriver object at 0x7f7bf40bfd60>

@DavidEBrumbaugh
Copy link
Author

@pushfoo - Indeed! I wasn't particularly interested in Pulse, I just didn't have OpenAL on this box. Just for the sake of anybody who finds this later... I ran this command to install OpenAL

sudo apt-get install libopenal-dev

@caffeinepills
Copy link
Collaborator

caffeinepills commented Sep 13, 2023

The bug is clearly in pulse audio. It was listed as a "closed issue":

Could you please you post the following?

  1. The version of AVbin pyglet is using
  2. The miniaudio version that AVbin is using
  3. Your Ubuntu version

Although the issue you linked was closed in 2020, the fix might not be shipped on older LTS versions.

AVbin hasn't been part of pyglet for a while.

I personally haven't had experience with PulseAudio, just OpenAL, Xaudio2, and DirectSound. I do know PulseAudio is probably one of the oldest drivers.

@pushfoo
Copy link
Contributor

pushfoo commented Sep 13, 2023

AVbin hasn't been part of pyglet for a while.

This was already corrected in this comment, my apologies.

sudo apt-get install libopenal-dev

Are you sure you need need the -dev version rather than just libopenal? I see the following on my system:

$ dpkg -l | grep openal
ii  libopenal-data                        1:1.19.1-2                       all          Software implementation of the OpenAL audio API (data files)
ii  libopenal1:amd64                      1:1.19.1-2                       amd64        Software implementation of the OpenAL audio API (shared library)

Either way, I think the doc solution is the best short-term option for arcade, if not pyglet. Thank you for reporting this and continuing to help with investigating it.

Even though it looks like one of the lead pyglet maintainers is already aware, the PulseAudio issues could still take a while to get resolved. Race condition bugs aren't really fun to deal with, especially when the easy workaround is to use an audio backend already present on many distros.

@DavidEBrumbaugh
Copy link
Author

@pushfoo I tend to install the -dev version of stuff by default old habit - you just never know when you want to tweak something.

@pushfoo
Copy link
Contributor

pushfoo commented Sep 13, 2023

tend to install the -dev version of stuff by default old habit - you just never know when you want to tweak something.

I think it would make sense for anyone looking to develop pyglet with a focus on audio. Have you worked with Sphinx before?

@DavidEBrumbaugh
Copy link
Author

@pushfoo no, I haven't. I'm old though ;). I had a few games published for the Playstation 1 in the late 1990s. These days I usually have guys working for me. I'm writing a zombie game for a five-year-old child of a good friend I know that loves Zombies. :)

@DavidEBrumbaugh
Copy link
Author

Ah, it's a documentation library!

@caffeinepills
Copy link
Collaborator

One thing you can do as well, is enable media debugging via pyglet.options["debug_media"] = True. This will produce a lot of debug messages, but may provide more information when the crash occurs.

@pushfoo
Copy link
Contributor

pushfoo commented Sep 13, 2023

I'm old though ;).

Your experience with tech hype cycles means you'll be ready when if another AI winter hits.

Arcade's recent git history demonstrates the downside of Sphinx: it's powerful, yet unintuitive at best and painful at worst. However, it's probably nothing compared to the PS1 optimization and disc layout stories I've heard. :P

I'd welcome your input on either of the referencing issues linked above, or any other arcade issues. Knowledge of OOP and abstraction pitfalls from the past would be helpful to avoid repeating them.

One thing you can do as well, is enable media debugging

Thank you for pointing that out. I'll include this in the arcade doc as well. As to pyglet, what's your recommendation for how to proceed? Doc for now, full fix later?

@pushfoo
Copy link
Contributor

pushfoo commented Sep 13, 2023

Highlights from a brief Discord discussion with Square789:

  1. At least some of the problems involved are well-understood

    i've only read the linked miniaudio fix which involved additional mainloop locks; so that may be it really
    i do definitely know there is at least one missing lock in the PA adaptation, i just don't remember where

  2. Temporary doc fixes are still a good idea

    That sounds good; openal is a lot more friendly to use anyways

@caffeinepills
Copy link
Collaborator

I would go ahead and give the latest version a try as there has been a big rework in the audio engine (2.0.14b). Hopefully that clears up the issue, if not let us know. If we haven't heard back we will assume this is stale and close this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants