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 #include "SDL.h"
13 
14 #include <stdio.h> /* for fflush() and stdout */
15 
16 #ifdef __EMSCRIPTEN__
17 #include <emscripten/emscripten.h>
18 #endif
19 
20 static SDL_AudioSpec spec;
21 static Uint8 *sound = NULL;     /* Pointer to wave data */
22 static Uint32 soundlen = 0;     /* Length of wave data */
23 
24 typedef struct
25 {
26     SDL_AudioDeviceID dev;
27     int soundpos;
28     SDL_atomic_t done;
29 } callback_data;
30 
31 callback_data cbd[64];
32 
33 void SDLCALL
play_through_once(void * arg,Uint8 * stream,int len)34 play_through_once(void *arg, Uint8 * stream, int len)
35 {
36     callback_data *cbd = (callback_data *) arg;
37     Uint8 *waveptr = sound + cbd->soundpos;
38     int waveleft = soundlen - cbd->soundpos;
39     int cpy = len;
40     if (cpy > waveleft)
41         cpy = waveleft;
42 
43     SDL_memcpy(stream, waveptr, cpy);
44     len -= cpy;
45     cbd->soundpos += cpy;
46     if (len > 0) {
47         stream += cpy;
48         SDL_memset(stream, spec.silence, len);
49         SDL_AtomicSet(&cbd->done, 1);
50     }
51 }
52 
53 void
loop()54 loop()
55 {
56     if (SDL_AtomicGet(&cbd[0].done)) {
57 #ifdef __EMSCRIPTEN__
58         emscripten_cancel_main_loop();
59 #endif
60         SDL_PauseAudioDevice(cbd[0].dev, 1);
61         SDL_CloseAudioDevice(cbd[0].dev);
62         SDL_FreeWAV(sound);
63         SDL_Quit();
64     }
65 }
66 
67 static void
test_multi_audio(int devcount)68 test_multi_audio(int devcount)
69 {
70     int keep_going = 1;
71     int i;
72 
73 #ifdef __ANDROID__
74     SDL_Event event;
75 
76     /* Create a Window to get fully initialized event processing for testing pause on Android. */
77     SDL_CreateWindow("testmultiaudio", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 320, 240, 0);
78 #endif
79 
80     if (devcount > 64) {
81         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Too many devices (%d), clamping to 64...\n",
82                 devcount);
83         devcount = 64;
84     }
85 
86     spec.callback = play_through_once;
87 
88     for (i = 0; i < devcount; i++) {
89         const char *devname = SDL_GetAudioDeviceName(i, 0);
90         SDL_Log("playing on device #%d: ('%s')...", i, devname);
91         fflush(stdout);
92 
93         SDL_memset(&cbd[0], '\0', sizeof(callback_data));
94         spec.userdata = &cbd[0];
95         cbd[0].dev = SDL_OpenAudioDevice(devname, 0, &spec, NULL, 0);
96         if (cbd[0].dev == 0) {
97             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Open device failed: %s\n", SDL_GetError());
98         } else {
99             SDL_PauseAudioDevice(cbd[0].dev, 0);
100 #ifdef __EMSCRIPTEN__
101             emscripten_set_main_loop(loop, 0, 1);
102 #else
103             while (!SDL_AtomicGet(&cbd[0].done)) {
104                 #ifdef __ANDROID__
105                 /* Empty queue, some application events would prevent pause. */
106                 while (SDL_PollEvent(&event)){}
107                 #endif
108                 SDL_Delay(100);
109             }
110             SDL_PauseAudioDevice(cbd[0].dev, 1);
111 #endif
112             SDL_Log("done.\n");
113             SDL_CloseAudioDevice(cbd[0].dev);
114         }
115     }
116 
117     SDL_memset(cbd, '\0', sizeof(cbd));
118 
119     SDL_Log("playing on all devices...\n");
120     for (i = 0; i < devcount; i++) {
121         const char *devname = SDL_GetAudioDeviceName(i, 0);
122         spec.userdata = &cbd[i];
123         cbd[i].dev = SDL_OpenAudioDevice(devname, 0, &spec, NULL, 0);
124         if (cbd[i].dev == 0) {
125             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Open device %d failed: %s\n", i, SDL_GetError());
126         }
127     }
128 
129     for (i = 0; i < devcount; i++) {
130         if (cbd[i].dev) {
131             SDL_PauseAudioDevice(cbd[i].dev, 0);
132         }
133     }
134 
135     while (keep_going) {
136         keep_going = 0;
137         for (i = 0; i < devcount; i++) {
138             if ((cbd[i].dev) && (!SDL_AtomicGet(&cbd[i].done))) {
139                 keep_going = 1;
140             }
141         }
142         #ifdef __ANDROID__
143         /* Empty queue, some application events would prevent pause. */
144         while (SDL_PollEvent(&event)){}
145         #endif
146 
147         SDL_Delay(100);
148     }
149 
150 #ifndef __EMSCRIPTEN__
151     for (i = 0; i < devcount; i++) {
152         if (cbd[i].dev) {
153             SDL_PauseAudioDevice(cbd[i].dev, 1);
154             SDL_CloseAudioDevice(cbd[i].dev);
155         }
156     }
157 
158     SDL_Log("All done!\n");
159 #endif
160 }
161 
162 
163 int
main(int argc,char ** argv)164 main(int argc, char **argv)
165 {
166     int devcount = 0;
167 
168     /* Enable standard application logging */
169     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
170 
171     /* Load the SDL library */
172     if (SDL_Init(SDL_INIT_AUDIO) < 0) {
173         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
174         return (1);
175     }
176 
177     SDL_Log("Using audio driver: %s\n", SDL_GetCurrentAudioDriver());
178 
179     devcount = SDL_GetNumAudioDevices(0);
180     if (devcount < 1) {
181         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Don't see any specific audio devices!\n");
182     } else {
183         if (argv[1] == NULL) {
184             argv[1] = "sample.wav";
185         }
186 
187         /* Load the wave file into memory */
188         if (SDL_LoadWAV(argv[1], &spec, &sound, &soundlen) == NULL) {
189             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", argv[1],
190                     SDL_GetError());
191         } else {
192             test_multi_audio(devcount);
193             SDL_FreeWAV(sound);
194         }
195     }
196 
197     SDL_Quit();
198     return 0;
199 }
200