jakebesworth/Simple-SDL2-Audio

Emscripten

claimred opened this issue ยท 3 comments

Great example @jakebesworth! ๐Ÿ‘ Really helped me to get audio working in SDL2 (SDL2_mixer is not available in emcc afaik)

One thing I noticed though is that it just won't work with Emscripten, tried playing around with your code and it seems that changing SDL_AUDIO_ALLOW_ANY_CHANGE to SDL_AUDIO_ALLOW_FREQUENCY_CHANGE solved the issue, at least for Chrome, Edge and Safari. I don't know anything about how sound works, so I can't explain why.

Hey claimred, thank you for the kind issue, I'm glad my code helped you!

So for initAudio() the line: if((gDevice->device = SDL_OpenAudioDevice(NULL, 0, &(gDevice->want), NULL, SDL_AUDIO_ALLOW_ANY_CHANGE)) == 0) attempts to open whatever the first available audio device is.

If that line fails, it should print an error to stderr did you notice any error specifically? (Also I should add a line that ends SDL2-simple-audio if that line fails...)

https://wiki.libsdl.org/SDL_OpenAudioDevice Specifies that:

These flags specify how SDL should behave when a device cannot offer a specific feature. If the application requests a feature that the hardware doesn't offer, SDL will always try to get the closest equivalent.

For example, if you ask for float32 audio format, but the sound card only supports int16, SDL will set the hardware to int16. If you had set SDL_AUDIO_ALLOW_FORMAT_CHANGE, SDL will change the format in the obtained structure. If that flag was not set, SDL will prepare to convert your callback's float32 audio to int16 before feeding it to the hardware and will keep the originally requested format in the obtained structure.

If your application can only handle one specific data format, pass a zero for allowed_changes and let SDL transparently handle any differences.

An opened audio device starts out paused, and should be enabled for playing by calling SDL_PauseAudioDevice(devid, 0) when you are ready for your audio callback function to be called. Since the audio driver may modify the requested size of the audio buffer, you should allocate any local mixing buffers after you open the audio device.

The audio callback runs in a separate thread in most cases; you can prevent race conditions between your callback and other threads without fully pausing playback with SDL_LockAudioDevice(). For more information about the callback, see SDL_AudioSpec. 

What this means to me is that either SDL_AUDIO_ALLOW_CHANNELS_CHANGE or SDL_AUDIO_ALLOW_FORMAT_CHANGE are blowing up your program.

If you use: mplayer -identify music.wav on the audio file, what are the channels and format?

Are they:

#define AUDIO_FORMAT AUDIO_S16LSB // Signed 16 bit Little Endian
#define AUDIO_CHANNELS 2

You may wish to change those values if they're not the same.

The flag SDL_AUDIO_ALLOW_ANY_CHANGE is defined as:

#define SDL_AUDIO_ALLOW_FREQUENCY_CHANGE    0x00000001
#define SDL_AUDIO_ALLOW_FORMAT_CHANGE       0x00000002
#define SDL_AUDIO_ALLOW_CHANNELS_CHANGE     0x00000004
#define SDL_AUDIO_ALLOW_ANY_CHANGE (SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_FORMAT_CHANGE|SDL_AUDIO_ALLOW_CHANNELS_CHANGE)

See if you can change SDL_AUDIO_ALLOW_ANY_CHANGE to 0, see if that works?

For this, I might want to make allowed_changes a configured value at the top of file, maybe even default to 0. The way this works is that even if you use a 16bit LE audio with a 32bit LE config value, it will still play, because SDL2 will morph the data structure.

Thank you very much for bringing this to my attention, let me know if you get an error, or if the 0 also works, as I don't have emcc to test myself.

Also TODO for me:

  • Add play and pause functions
  • Explain new 2.0.5 method for audio which isn't callback (maybe even implement it)
  • Make this issue a config value and force the initAudio to die on failed open audio device

ffmpeg -i music.wav

Yep, used that to change AUDIO_FREQUENCY from 44800 to 44100 for my files. It worked with Win32 app without the change though.

Sorry, I completely forgot to clarify what I meant by "won't work". There are no errors, yet the audio output freezes up after maybe a second or two of playing. Playing last "note" (maybe?) over and over again. Even the whole app tab freezes. This thing reproduces really weird. I got it tested on a couple of Win10 machines and on a Mac. Everything went 100% fine only on one Win 10 pc.

Need to find a way to reproduce the bug reliably and provide you with a clear test case, not sure if it's an easy thing to do though. Also, it might be related to that emscripten-core/emscripten#3477 issue. I don't really understand what "floats" they are talking about, but maybe you will find something useful since you understand sound.

I've closed this issue for now, because the new config value at the top should give more notice to users, and they can change it themselves, such as when using Emscripten. For simplicity of the library I can't added a check, but a user config makes more sense.

If you can find a way to reproduce the bug reliably / real test case @claimred I can help delve deeper into the issue at hand, but my guess is that SDL2 can't convert some differing audio files based on your audio device of your machine, and it dies.