123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- /*
- Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely.
- */
- /* Program to test hotplugging of audio devices */
- #include <SDL3/SDL.h>
- #include <SDL3/SDL_main.h>
- #include <SDL3/SDL_test.h>
- #include "testutils.h"
- #ifdef SDL_PLATFORM_EMSCRIPTEN
- #include <emscripten/emscripten.h>
- #endif
- #include <stdlib.h>
- #ifdef HAVE_SIGNAL_H
- #include <signal.h>
- #endif
- static SDL_AudioSpec spec;
- static Uint8 *sound = NULL; /* Pointer to wave data */
- static Uint32 soundlen = 0; /* Length of wave data */
- static SDLTest_CommonState *state;
- /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
- static void
- quit(int rc)
- {
- SDL_Quit();
- SDLTest_CommonDestroyState(state);
- /* Let 'main()' return normally */
- if (rc != 0) {
- exit(rc);
- }
- }
- static int done = 0;
- static void poked(int sig)
- {
- done = 1;
- }
- static const char *devtypestr(int recording)
- {
- return recording ? "recording" : "playback";
- }
- static void iteration(void)
- {
- SDL_Event e;
- SDL_AudioDeviceID dev;
- while (SDL_PollEvent(&e)) {
- if (e.type == SDL_EVENT_QUIT) {
- done = 1;
- } else if (e.type == SDL_EVENT_KEY_UP) {
- if (e.key.key == SDLK_ESCAPE) {
- done = 1;
- }
- } else if (e.type == SDL_EVENT_AUDIO_DEVICE_ADDED) {
- const SDL_AudioDeviceID which = (SDL_AudioDeviceID) e.adevice.which;
- const bool recording = e.adevice.recording ? true : false;
- const char *name = SDL_GetAudioDeviceName(which);
- if (name) {
- SDL_Log("New %s audio device at id %u: %s", devtypestr(recording), (unsigned int)which, name);
- } else {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Got new %s device, id %u, but failed to get the name: %s",
- devtypestr(recording), (unsigned int)which, SDL_GetError());
- continue;
- }
- if (!recording) {
- SDL_AudioStream *stream = SDL_OpenAudioDeviceStream(which, &spec, NULL, NULL);
- if (!stream) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create/bind an audio stream to %u ('%s'): %s", (unsigned int) which, name, SDL_GetError());
- } else {
- SDL_Log("Opened '%s' as %u\n", name, (unsigned int) which);
- /* !!! FIXME: laziness, this used to loop the audio, but we'll just play it once for now on each connect. */
- SDL_PutAudioStreamData(stream, sound, soundlen);
- SDL_FlushAudioStream(stream);
- SDL_ResumeAudioStreamDevice(stream);
- /* !!! FIXME: this is leaking the stream for now. We'll wire it up to a dictionary or whatever later. */
- }
- }
- } else if (e.type == SDL_EVENT_AUDIO_DEVICE_REMOVED) {
- dev = (SDL_AudioDeviceID)e.adevice.which;
- SDL_Log("%s device %u removed.\n", devtypestr(e.adevice.recording), (unsigned int)dev);
- /* !!! FIXME: we need to keep track of our streams and destroy them here. */
- }
- }
- }
- #ifdef SDL_PLATFORM_EMSCRIPTEN
- static void loop(void)
- {
- if (done)
- emscripten_cancel_main_loop();
- else
- iteration();
- }
- #endif
- int main(int argc, char *argv[])
- {
- int i;
- char *filename = NULL;
- SDL_Window *window;
- /* Initialize test framework */
- state = SDLTest_CommonCreateState(argv, 0);
- if (!state) {
- return 1;
- }
- /* Parse commandline */
- for (i = 1; i < argc;) {
- int consumed;
- consumed = SDLTest_CommonArg(state, i);
- if (!consumed) {
- if (!filename) {
- filename = argv[i];
- consumed = 1;
- }
- }
- if (consumed <= 0) {
- static const char *options[] = { "[sample.wav]", NULL };
- SDLTest_CommonLogUsage(state, argv[0], options);
- exit(1);
- }
- i += consumed;
- }
- /* Load the SDL library */
- if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
- return 1;
- }
- /* Some targets (Mac CoreAudio) need an event queue for audio hotplug, so make and immediately hide a window. */
- window = SDL_CreateWindow("testaudiohotplug", 640, 480, 0);
- if (!window) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_CreateWindow failed: %s\n", SDL_GetError());
- quit(1);
- }
- SDL_MinimizeWindow(window);
- filename = GetResourceFilename(filename, "sample.wav");
- if (!filename) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s\n", SDL_GetError());
- quit(1);
- }
- /* Load the wave file into memory */
- if (!SDL_LoadWAV(filename, &spec, &sound, &soundlen)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, SDL_GetError());
- quit(1);
- }
- #ifdef HAVE_SIGNAL_H
- /* Set the signals */
- #ifdef SIGHUP
- (void)signal(SIGHUP, poked);
- #endif
- (void)signal(SIGINT, poked);
- #ifdef SIGQUIT
- (void)signal(SIGQUIT, poked);
- #endif
- (void)signal(SIGTERM, poked);
- #endif /* HAVE_SIGNAL_H */
- /* Show the list of available drivers */
- SDL_Log("Available audio drivers:");
- for (i = 0; i < SDL_GetNumAudioDrivers(); ++i) {
- SDL_Log("%i: %s", i, SDL_GetAudioDriver(i));
- }
- SDL_Log("Select a driver with the SDL_AUDIO_DRIVER environment variable.\n");
- SDL_Log("Using audio driver: %s\n", SDL_GetCurrentAudioDriver());
- #ifdef SDL_PLATFORM_EMSCRIPTEN
- emscripten_set_main_loop(loop, 0, 1);
- #else
- while (!done) {
- SDL_Delay(100);
- iteration();
- }
- #endif
- /* Clean up on signal */
- /* Quit audio first, then free WAV. This prevents access violations in the audio threads. */
- SDL_QuitSubSystem(SDL_INIT_AUDIO);
- SDL_free(sound);
- SDL_free(filename);
- quit(0);
- return 0;
- }
|