1 /*
2   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
3 
4   This software is provided 'as-is', without any express or implied
5   warranty.  In no event will the authors be held liable for any damages
6   arising from the use of this software.
7 
8   Permission is granted to anyone to use this software for any purpose,
9   including commercial applications, and to alter it and redistribute it
10   freely.
11 */
12 
13 /* Program to test hotplugging of audio devices */
14 
15 #include "SDL_config.h"
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 
20 #if HAVE_SIGNAL_H
21 #include <signal.h>
22 #endif
23 
24 #ifdef __EMSCRIPTEN__
25 #include <emscripten/emscripten.h>
26 #endif
27 
28 #include "SDL.h"
29 
30 static SDL_AudioSpec spec;
31 static Uint8 *sound = NULL;     /* Pointer to wave data */
32 static Uint32 soundlen = 0;     /* Length of wave data */
33 
34 static int posindex = 0;
35 static Uint32 positions[64];
36 
37 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
38 static void
quit(int rc)39 quit(int rc)
40 {
41     SDL_Quit();
42     exit(rc);
43 }
44 
45 void SDLCALL
fillerup(void * _pos,Uint8 * stream,int len)46 fillerup(void *_pos, Uint8 * stream, int len)
47 {
48     Uint32 pos = *((Uint32 *) _pos);
49     Uint8 *waveptr;
50     int waveleft;
51 
52     /* Set up the pointers */
53     waveptr = sound + pos;
54     waveleft = soundlen - pos;
55 
56     /* Go! */
57     while (waveleft <= len) {
58         SDL_memcpy(stream, waveptr, waveleft);
59         stream += waveleft;
60         len -= waveleft;
61         waveptr = sound;
62         waveleft = soundlen;
63         pos = 0;
64     }
65     SDL_memcpy(stream, waveptr, len);
66     pos += len;
67     *((Uint32 *) _pos) = pos;
68 }
69 
70 static int done = 0;
71 void
poked(int sig)72 poked(int sig)
73 {
74     done = 1;
75 }
76 
77 static const char*
devtypestr(int iscapture)78 devtypestr(int iscapture)
79 {
80     return iscapture ? "capture" : "output";
81 }
82 
83 static void
iteration()84 iteration()
85 {
86     SDL_Event e;
87     SDL_AudioDeviceID dev;
88     while (SDL_PollEvent(&e)) {
89         if (e.type == SDL_QUIT) {
90             done = 1;
91         } else if (e.type == SDL_KEYUP) {
92             if (e.key.keysym.sym == SDLK_ESCAPE)
93                 done = 1;
94         } else if (e.type == SDL_AUDIODEVICEADDED) {
95             int index = e.adevice.which;
96             int iscapture = e.adevice.iscapture;
97             const char *name = SDL_GetAudioDeviceName(index, iscapture);
98             if (name != NULL)
99                 SDL_Log("New %s audio device at index %u: %s\n", devtypestr(iscapture), (unsigned int) index, name);
100             else {
101                 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Got new %s device at index %u, but failed to get the name: %s\n",
102                     devtypestr(iscapture), (unsigned int) index, SDL_GetError());
103                 continue;
104             }
105             if (!iscapture) {
106                 positions[posindex] = 0;
107                 spec.userdata = &positions[posindex++];
108                 spec.callback = fillerup;
109                 dev = SDL_OpenAudioDevice(name, 0, &spec, NULL, 0);
110                 if (!dev) {
111                     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open '%s': %s\n", name, SDL_GetError());
112                 } else {
113                     SDL_Log("Opened '%s' as %u\n", name, (unsigned int) dev);
114                     SDL_PauseAudioDevice(dev, 0);
115                 }
116             }
117         } else if (e.type == SDL_AUDIODEVICEREMOVED) {
118             dev = (SDL_AudioDeviceID) e.adevice.which;
119             SDL_Log("%s device %u removed.\n", devtypestr(e.adevice.iscapture), (unsigned int) dev);
120             SDL_CloseAudioDevice(dev);
121         }
122     }
123 }
124 
125 #ifdef __EMSCRIPTEN__
126 void
loop()127 loop()
128 {
129     if(done)
130         emscripten_cancel_main_loop();
131     else
132         iteration();
133 }
134 #endif
135 
136 int
main(int argc,char * argv[])137 main(int argc, char *argv[])
138 {
139     int i;
140     char filename[4096];
141 
142     /* Enable standard application logging */
143     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
144 
145     /* Load the SDL library */
146     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
147         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
148         return (1);
149     }
150 
151     /* Some targets (Mac CoreAudio) need an event queue for audio hotplug, so make and immediately hide a window. */
152     SDL_MinimizeWindow(SDL_CreateWindow("testaudiohotplug", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0));
153 
154     if (argc > 1) {
155         SDL_strlcpy(filename, argv[1], sizeof(filename));
156     } else {
157         SDL_strlcpy(filename, "sample.wav", sizeof(filename));
158     }
159     /* Load the wave file into memory */
160     if (SDL_LoadWAV(filename, &spec, &sound, &soundlen) == NULL) {
161         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, SDL_GetError());
162         quit(1);
163     }
164 
165 #if HAVE_SIGNAL_H
166     /* Set the signals */
167 #ifdef SIGHUP
168     signal(SIGHUP, poked);
169 #endif
170     signal(SIGINT, poked);
171 #ifdef SIGQUIT
172     signal(SIGQUIT, poked);
173 #endif
174     signal(SIGTERM, poked);
175 #endif /* HAVE_SIGNAL_H */
176 
177     /* Show the list of available drivers */
178     SDL_Log("Available audio drivers:");
179     for (i = 0; i < SDL_GetNumAudioDrivers(); ++i) {
180         SDL_Log("%i: %s", i, SDL_GetAudioDriver(i));
181     }
182 
183     SDL_Log("Select a driver with the SDL_AUDIODRIVER environment variable.\n");
184     SDL_Log("Using audio driver: %s\n", SDL_GetCurrentAudioDriver());
185 
186 #ifdef __EMSCRIPTEN__
187     emscripten_set_main_loop(loop, 0, 1);
188 #else
189     while (!done) {
190         SDL_Delay(100);
191         iteration();
192     }
193 #endif
194 
195     /* Clean up on signal */
196     /* Quit audio first, then free WAV. This prevents access violations in the audio threads. */
197     SDL_QuitSubSystem(SDL_INIT_AUDIO);
198     SDL_FreeWAV(sound);
199     SDL_Quit();
200     return (0);
201 }
202 
203 /* vi: set ts=4 sw=4 expandtab: */
204