raylib icon indicating copy to clipboard operation
raylib copied to clipboard

[raudio] Crash when changing playback device

Open thabetx opened this issue 1 year ago • 8 comments

Issue description

Example (audio_sound_loading.c) crashes when changing playback device,.

Environment

This happens on Windows.

Initialization Logs

INFO: Initializing raylib 5.1-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: 1600 x 900 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: 230 INFO: GL: OpenGL device information: INFO: > Vendor: Intel INFO: > Renderer: Intel(R) UHD Graphics INFO: > Version: 3.3.0 - Build 27.20.100.8190 INFO: > GLSL: 3.30 - Build 27.20.100.8190 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: GL: ASTC 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) 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: FILEIO: [resources/sound.wav] File loaded successfully INFO: WAVE: Data loaded successfully (44100 Hz, 16 bit, 1 channels) INFO: FILEIO: [resources/target.ogg] File loaded successfully INFO: WAVE: Data loaded successfully (22050 Hz, 16 bit, 2 channels) INFO: TIMER: Target time per frame: 16.667 milliseconds

Also note that miniaudio has a similar example (simple_playback.c) that doesn't have an issue with switching playback devices.

Issue Screenshot

https://github.com/raysan5/raylib/assets/7282243/6cebb61c-ec89-41f9-86df-b666491a1881

Code Example

This happened to me on some personal projects and happens with the audio_sound_loading example.

thabetx avatar Jan 20 '24 13:01 thabetx

@thabetx Thanks for reporting, raudio initializes some buffers internally and the player thread, probably the crash is related to some missing mutex or not properly managing the device change... It requires some review.

raysan5 avatar Jan 31 '24 23:01 raysan5

I looked into this a bit and here's what I found: in miniaudio.h -> ma_context_get_MMDevice__wasapi there is a call to ma_CoCreateInstance . It works initially on launch of the program when it's called from the main thread. When default audio device is changed ma_context_get_MMDevice__wasapi is eventually called from ma_IMMNotificationClient_OnDefaultDeviceChanged callback, from a different thread, which causes ma_CoCreateInstance to fail, because there was no ma_CoInitializeEx call from this thread. I haven't debug miniaudio simple_playback.c example, so I don't know how it works there.

veins1 avatar Feb 04 '24 15:02 veins1

Quick question @raysan5.

I looked into miniaudio and noticed that it can return a list of audio devices and to use a particular device. I currently do not see that feature in raylib. Is that something that we could get added to raylib?

I have an audio library that I built called CASL that my 2D game framework Velaptor uses for audio. Currently, CASL provides the ability to enumerate audio devices and switch to them. I was hoping to be able to use RayLib for audio under the hood in CASL instead of what it currently uses which is OpenAL.

Would this possibly be a feature added into RayLib?

CalvinWilkinson avatar Feb 05 '24 11:02 CalvinWilkinson

Here is some info to possibly help with this.

Using this sample, I created a quick app to get familiar with how sound is done with RayLib using the C# binding project.

In my Windows system, I have the built-in system audio which is a Realtek Audio device. I also have a pair of Sony noise-canceling headphones which is the WH-1000XM5 model.

I connected the headphones and played the sample project and everything worked great. As the sound was playing, I turned off the headphones. I expected the app to crash, but to my surprise, it did not and just automatically started playing through the Realtek Audio device on the laptop. I also turned the headphones back on and it automatically changed from the laptop audio device back to the Sony headphones with no issue.

I hope this helps you out!!

Extra info: OS: Windows 11 PRO 23H2 (OS Build 22631.3085) C#/dotnet: v8.0

Example Source Code Used

using Raylib_cs;

const int screenWidth = 800;
const int screenHeight = 450;

Raylib.InitWindow(screenWidth, screenHeight, "Sound Testing");

Raylib.InitAudioDevice();

var music = Raylib.LoadMusicStream("Content/Audio/brinstar.mp3");

Raylib.PlayMusicStream(music);

var timePlayed = 0f;
var pause = false;

music.Looping = true;

Raylib.SetTargetFPS(30);

while (!Raylib.WindowShouldClose())
{
    Raylib.UpdateMusicStream(music);

    // Restart music playing (stop and play)
    if (Raylib.IsKeyPressed(KeyboardKey.Space))
    {
        Raylib.StopMusicStream(music);
        Raylib.PlayMusicStream(music);
    }

    // Pause/Resume music playing
    if (Raylib.IsKeyPressed(KeyboardKey.P))
    {
        pause = !pause;

        if (pause)
        {
            Raylib.PauseMusicStream(music);
        }
        else
        {
            Raylib.ResumeMusicStream(music);
        }
    }

    if (Raylib.IsKeyPressed(KeyboardKey.Right))
    {
        var currentPosition = Raylib.GetMusicTimePlayed(music);
        var newPosition = currentPosition + 10;
        Raylib.SeekMusicStream(music, newPosition);
    }

    if (Raylib.IsKeyPressed(KeyboardKey.Left))
    {
        var currentPosition = Raylib.GetMusicTimePlayed(music);
        var newPosition = currentPosition - 10;
        Raylib.SeekMusicStream(music, newPosition);
    }

    // Get normalized time played for current music stream
    timePlayed = Raylib.GetMusicTimePlayed(music)/Raylib.GetMusicTimeLength(music);

    if (timePlayed > 1.0f) timePlayed = 1.0f;   // Make sure time played is no longer than music
    //----------------------------------------------------------------------------------

    // Draw
    //----------------------------------------------------------------------------------
    Raylib.BeginDrawing();

    Raylib.DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, Color.LightGray);

    Raylib.DrawRectangle(200, 200, 400, 12, Color.LightGray);
    Raylib.DrawRectangle(200, 200, (int)(timePlayed*400.0f), 12, Color.Maroon);
    Raylib.DrawRectangleLines(200, 200, 400, 12, Color.Gray);

    Raylib.DrawText("PRESS SPACE TO RESTART MUSIC", 215, 250, 20, Color.LightGray);
    Raylib.DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 280, 20, Color.LightGray);

    Raylib.EndDrawing();
}

// De-Initialization
//--------------------------------------------------------------------------------------
Raylib.UnloadMusicStream(music);   // Unload music stream buffers from RAM
Raylib.CloseAudioDevice();         // Close audio device (music streaming is automatically stopped)
Raylib.CloseWindow();              // Close window and OpenGL context
//--------------------------------------------------------------------------------------

CalvinWilkinson avatar Feb 05 '24 22:02 CalvinWilkinson

This is likely related. When my monitor goes into sleep mode, my raylib game crashes with the following error. Windows 11 / raylib 5.0

Thread 14 received signal SIGSEGV, Segmentation fault. [Switching to Thread 6576.0x20ec] 0x00007ff7f64a26e9 in ma_device_start.wasapi_nolock ()

zapposh avatar Jun 15 '24 08:06 zapposh

Reminder that this issue is causing shipped games to crash - happens for example when bluetooth headphones auto-connect to the PC.

jkaup avatar Jun 24 '24 18:06 jkaup

@jkaup thanks for the reminder, feel free to review it and send a fix PR. thanks.

raysan5 avatar Jun 24 '24 19:06 raysan5

@jkaup thanks for the reminder, feel free to send a fix as soon as possible. waiting for your PR. thanks.

I will take a look in case this is not being worked on - however note that I have 0 prior experience with audio. I'll send a PR if I have any solution to this.

jkaup avatar Jun 24 '24 19:06 jkaup