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

Crash when changing default playback device. #764

Open
mackron opened this issue Nov 1, 2023 · 13 comments
Open

Crash when changing default playback device. #764

mackron opened this issue Nov 1, 2023 · 13 comments

Comments

@mackron
Copy link
Owner

mackron commented Nov 1, 2023

raysan5/raylib#3489

CC @jestarray

I've been unable to replicate this. Are you able to try miniaudio's simple_playback_sine example? It's easy to compile - there's no dependencies or anything. It'll just play a sine wave, and it'll attempt to reroute audio when you try to change the default device.

Could you also show me the contents of the ma_device object at the time of the crash? There's not quite enough information for me to go on so far.

Is this 100% reproducible for you?

@jestarray
Copy link

onecore\com\combase\objact\objact.cxx(826)\combase.dll!00007FFFD296DC99: (caller: 00007FFFD296CFBA) ReturnHr(1) tid(3a38) 800401F0 CoInitialize has not been called.
  Name Value Type
pDevice audio_music_stream.exe!0x00007ff6cc198170 {pContext=0x00007ff6cc197ea0 {audio_music_stream.exe!AudioData AUDIO} {...} ...} ma_device *
  ▶ pContext 0x00007ff6cc197ea0 {audio_music_stream.exe!AudioData AUDIO} {callbacks={onContextInit=0x00007ff6cbf9b460 {audio_music_stream.exe!ma_context_init__wasapi(ma_context *, const ma_context_config *, ma_backend_callbacks *)} ...} ...} ma_context *
  type ma_device_type_playback (1) ma_device_type
  sampleRate 48000 unsigned int
  ▶ state {value=ma_device_state_starting (3) } ma_atomic_device_state
  onData 0x00007ff6cbf3c010 {audio_music_stream.exe!OnSendAudioDataToDevice(ma_device *, void *, const void *, unsigned int)} void(*)(ma_device *, void *, const void *, unsigned int)
  onNotification 0x0000000000000000 void(*)(const ma_device_notification *)
  onStop 0x0000000000000000 void(*)(ma_device *)
  pUserData 0x0000000000000000 void *
  startStopLock 0x00000000000005d4 void *
  wakeupEvent 0x00000000000005d8 void *
  startEvent 0x00000000000005dc void *
  stopEvent 0x00000000000005e0 void *
  thread 0x0000000000000688 void *
  workResult MA_SUCCESS (0) ma_result
  isOwnerOfContext 0 '\0' unsigned char
  noPreSilencedOutputBuffer 0 '\0' unsigned char
  noClip 0 '\0' unsigned char
  noDisableDenormals 0 '\0' unsigned char
  noFixedSizedCallback 0 '\0' unsigned char
  ▶ masterVolumeFactor {value=1.00000000 } ma_atomic_float
  ▶ duplexRB {rb={ds={vtable=0x0000000000000000 {...} rangeBegInFrames=0 rangeEndInFrames=0 ...} rb={pBuffer=0x0000000000000000 {...} ...} ...} } ma_duplex_rb
  ▶ resampling {algorithm=ma_resample_algorithm_linear (0) pBackendVTable=0x0000000000000000 pBackendUserData=...}
  ▶ playback {pID=0x0000000000000000 id={wasapi=0x00007ff6cc1982a0 L"{0.0.0.00000000}.{6f836b77-268f-482d-838e-9f6df82af4d6}" ...} ...}
  ▶ capture {pID=0x0000000000000000 id={wasapi=0x00007ff6cc198838 L"" dsound=0x00007ff6cc198838 "" winmm=...} ...}
  ▶ wasapi {pAudioClientPlayback=0x0000000000000000 pAudioClientCapture=0x0000000000000000 pRenderClient=0x0000000000000000 ...}
  ▶ dsound {pPlayback=0x0000000000000000 pPlaybackPrimaryBuffer=0x0000000000000000 pPlaybackBuffer=0x0000000000000000 ...}
  ▶ winmm {hDevicePlayback=0x0000000000000000 hDeviceCapture=0x0000000000000000 hEventPlayback=0x0000000000000000 ...}
  ▶ jack {pClient=0x0000000000000000 ppPortsPlayback=0x0000000000000000 {???} ppPortsCapture=0x0000000000000000 {...} ...}
  ▶ null_device {deviceThread=0x0000000000000000 operationEvent=0x0000000000000000 operationCompletionEvent=0x0000000000000000 ...}

So I could not reproduce it with simple_playback_sine example(compiled with zig cc) interestingly but I could reproduce it with raylibs audio_music_stream on master branch:

https://github.com/raysan5/raylib/blob/master/examples/audio/audio_music_stream.c

https://github.com/raysan5/raylib/blob/master/src/external/miniaudio.h

2023-11-01_20-52-28.mp4

@mackron
Copy link
Owner Author

mackron commented Nov 2, 2023

OK, it's definitely suspicious that it works fine with simple_playback_sine. I can see at the time of the crash that pAudioClientPlayer and pRenderClient in the wasapi structure is NULL which is a concern, and most likely what's causing this crash. I just don't understand why that's the case in raylib but not simple_playback_sine.

Are you compiling raylib from source? If so, there's the commented line #define MA_DEBUG_OUTPUT in raudio.c. Would you be able to uncomment that, recompile, and the post the output? I wonder if maybe that might give us some more intelligence as to what's going on. It'll print stuff to stdout.

In src/raudio.c:

#define MINIAUDIO_IMPLEMENTATION
//#define MA_DEBUG_OUTPUT // <-- Try uncommenting this.
#include "external/miniaudio.h"

@jestarray
Copy link

jestarray commented Nov 2, 2023

INFO: Initializing raylib 5.0-dev
INFO: Platform backend: DESKTOP (GLFW)
INFO: Supported raylib modules:
INFO:     > rcore:..... loaded (mandatory)
INFO:     > rlgl:...... loaded (mandatory)
INFO:     > rshapes:... loaded (optional)
INFO:     > rtextures:. loaded (optional)
INFO:     > rtext:..... loaded (optional)
INFO:     > rmodels:... loaded (optional)
INFO:     > raudio:.... loaded (optional)
INFO: DISPLAY: Device initialized successfully
INFO:     > Display size: 3840 x 2160
INFO:     > Screen size:  800 x 450
INFO:     > Render size:  800 x 450
INFO:     > Viewport offsets: 0, 0
INFO: GLAD: OpenGL extensions loaded successfully
INFO: GL: Supported extensions count: 285
INFO: GL: OpenGL device information:
INFO:     > Vendor:   ATI Technologies Inc.
INFO:     > Renderer: Radeon RX 580 Series
INFO:     > Version:  3.3.0 Core Profile Context 23.9.2.230827
INFO:     > GLSL:     4.60
INFO: GL: VAO extension detected, VAO functions loaded successfully
INFO: GL: NPOT textures extension detected, full NPOT textures supported
INFO: GL: DXT compressed textures supported
INFO: GL: ETC2/EAC compressed textures supported
INFO: PLATFORM: DESKTOP (GLFW): Initialized successfully
INFO: TEXTURE: [ID 1] Texture loaded successfully (1x1 | R8G8B8A8 | 1 mipmaps)
INFO: TEXTURE: [ID 1] Default texture loaded successfully
INFO: SHADER: [ID 1] Vertex shader compiled successfully
INFO: SHADER: [ID 2] Fragment shader compiled successfully
INFO: SHADER: [ID 3] Program shader loaded successfully
INFO: SHADER: [ID 3] Default shader loaded successfully
INFO: RLGL: Render batch vertex buffers loaded successfully in RAM (CPU)
INFO: RLGL: Render batch vertex buffers loaded successfully in VRAM (GPU)
INFO: RLGL: Default OpenGL state initialized successfully
INFO: TEXTURE: [ID 2] Texture loaded successfully (128x128 | GRAY_ALPHA | 1 mipmaps)
INFO: FONT: Default font loaded successfully (224 glyphs)
DEBUG: Loading library: user32.dll
DEBUG: Loading symbol: GetForegroundWindow
DEBUG: Loading symbol: GetDesktopWindow
DEBUG: Loading library: advapi32.dll
DEBUG: Loading symbol: RegOpenKeyExA
DEBUG: Loading symbol: RegCloseKey
DEBUG: Loading symbol: RegQueryValueExA
DEBUG: Loading library: ole32.dll
DEBUG: Loading symbol: CoInitialize
DEBUG: Loading symbol: CoInitializeEx
DEBUG: Loading symbol: CoUninitialize
DEBUG: Loading symbol: CoCreateInstance
DEBUG: Loading symbol: CoTaskMemFree
DEBUG: Loading symbol: PropVariantClear
DEBUG: Loading symbol: StringFromGUID2
DEBUG: Attempting to initialize WASAPI backend...
DEBUG: Loading library: kernel32.dll
DEBUG: Loading symbol: VerifyVersionInfoW
DEBUG: Loading symbol: VerSetConditionMask
DEBUG: Loading library: avrt.dll
DEBUG: Loading symbol: AvSetMmThreadCharacteristicsA
DEBUG: Loading symbol: AvRevertMmThreadCharacteristics
DEBUG: System Architecture:
DEBUG:   Endian: LE
DEBUG:   SSE2:   YES
DEBUG:   AVX2:   YES
DEBUG:   NEON:   NO
DEBUG: [WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=480)
DEBUG:     defaultPeriodInFrames=480
DEBUG:     fundamentalPeriodInFrames=32
DEBUG:     minPeriodInFrames=128
DEBUG:     maxPeriodInFrames=480
DEBUG: [WASAPI] Using IAudioClient3
DEBUG:     periodSizeInFramesOut=480
INFO: [WASAPI]
INFO:   Speakers (2- High Definition Audio Device) (Playback)
INFO:     Format:      32-bit IEEE Floating Point -> 32-bit IEEE Floating Point
INFO:     Channels:    2 -> 2
INFO:     Sample Rate: 48000 -> 48000
INFO:     Buffer Size: 480*3 (1440)
INFO:     Conversion:
INFO:       Pre Format Conversion:  NO
INFO:       Post Format Conversion: NO
INFO:       Channel Routing:        NO
INFO:       Resampling:             NO
INFO:       Passthrough:            YES
INFO:       Channel Map In:         {CHANNEL_FRONT_LEFT CHANNEL_FRONT_RIGHT}
INFO:       Channel Map Out:        {CHANNEL_FRONT_LEFT CHANNEL_FRONT_RIGHT}
INFO: AUDIO: Device initialized successfully
INFO:     > Backend:       miniaudio / WASAPI
INFO:     > Format:        32-bit IEEE Floating Point -> 32-bit IEEE Floating Point
INFO:     > Channels:      2 -> 2
INFO:     > Sample rate:   48000 -> 48000
INFO:     > Periods size:  1440
INFO: STREAM: Initialized successfully (44100 Hz, 32 bit, Stereo)
INFO: FILEIO: [resources/country.mp3] Music file loaded successfully
INFO:     > Sample rate:   44100 Hz
INFO:     > Sample size:   32 bits
INFO:     > Channels:      2 (Stereo)
INFO:     > Total frames:  4128768
INFO: TIMER: Target time per frame: 33.333 milliseconds
DEBUG: === CHANGING DEVICE ===
ERROR: [WASAPI] Failed to create IMMDeviceEnumerator.
WARNING: [WASAPI] Reinitializing device after route change failed.

and here is the call stack from visuals studio in case it might help:

Call Stack:
audio_music_stream.exe!ma_IAudioClient_Start(ma_IAudioClient * pThis) Line 20501	C
 	audio_music_stream.exe!ma_device_start__wasapi_nolock(ma_device * pDevice) Line 22971	C
 	audio_music_stream.exe!ma_device_start__wasapi(ma_device * pDevice) Line 22992	C
 	audio_music_stream.exe!ma_worker_thread(void * pData) Line 40868	C
 	audio_music_stream.exe!ma_thread_entry_proxy(void * pData) Line 16507	C

@mackron
Copy link
Owner Author

mackron commented Nov 2, 2023

Well this is a start:

DEBUG: === CHANGING DEVICE ===
ERROR: [WASAPI] Failed to create IMMDeviceEnumerator.
WARNING: [WASAPI] Reinitializing device after route change failed.

Now the question is why is that failing. At a minimum I'll need to review the code to better handle that - obviously when these particular errors are happening it's putting the device into an invalid state. Once that's addressed it might fix the crash, but the actual rerouting functionality still won't work. I still don't understand why you're getting those errors with raylib and not simple_playback_sine though.

@jestarray
Copy link

jestarray commented Nov 2, 2023

Yeah, its really strange.... It's reproducable on raylib by multiple people though.

@karl-zylinski
Copy link

Any updates on this? I need to ship a game using raylib soon, so if you need any help to reproduce it, then I'd gladly help in whatever way I can.

@jestarray
Copy link

jestarray commented Jan 19, 2024

Any updates on this? I need to ship a game using raylib soon, so if you need any help to reproduce it, then I'd gladly help in whatever way I can.

Can you reproduce it? It's unlikely your users are going to be switching audio context in the middle of games so its going to be a somewhat rare crash. Sucks but works!

@karl-zylinski
Copy link

karl-zylinski commented Jan 20, 2024

@jestarray I haven't tried reproducing it outside of Raylib, with Raylib it 100% crashes inside miniaudio, even with a tiny program like this:

#include "raylib/raylib.h"

int main() {
	InitAudioDevice();

	Music music = LoadMusicStream("some_song.ogg");
	PlayMusicStream(music);

	while (true) {
		UpdateMusicStream(music);
	}
}

(above program translated from Odin to C by hand, it might contain errors)

so its going to be a somewhat rare crash

I think it depends on the user. I often start games and realize I had my output set to speakers instead of my headphones or the other way around, and alt-tab out to change it with the game running (which is how I ran into this issue)

I don't have time to look into a miniaudio-only repro right now, but maybe in a few weeks.

@karl-zylinski
Copy link

An update on how frequent this can happen: Some laptops switch audio device when you plug the headphones in, i.e. they don't do a hardware switch of the audio, they actually instruct windows to switch audio output. So in that case the game will crash because you plug in the headphones when you notice the audio coming out of your laptop speakers.

@raysan5
Copy link
Sponsor Contributor

raysan5 commented Feb 2, 2024

Related issue: raysan5/raylib#3743

@veins1
Copy link

veins1 commented Feb 4, 2024

Here's my attempt to figure this out: raysan5/raylib#3743 (comment)
Maybe I'm wrong in my conclusion, but surrounding ma_CoCreateInstance with ma_CoInitializeEx and ma_CoUninitialize stops the crashing and correctly switches audio device.

@mackron
Copy link
Owner Author

mackron commented Feb 5, 2024

Thanks for the heads up on the CoCreateInstance() thing. I've got plans to update the WASAPI backend so that every single WASAPI call gets deferred to a dedicated WASAPI thread. I think WASAPI is not only thread-unsafe, but downright thread hostile. Perhaps when that's done this will be fixed naturally. The thing is, that's a big change and I just haven't had the chance to get to it. But I suppose I could do the CoInitializeEx()/CoUninitialize() wrapper like you mentioned as a temporary workaround and see how the feedback with that goes.

@karl-zylinski
Copy link

Hi,
I put in the work-around above and it does indeed make my game not crash when I switch audio device.

I wrapped that call to CoCreateInstance with CoInitializeEx()/CoUninitialize() like you said. Do you think there may be any other issues because of this?

I'm looking into this because I am shipping my game on Steam in 5 days and I would love to not have this crash then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants