Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/test/browser/test_sdl2_audio_beep.cpp
6162 views
1
// Copyright 2014 The Emscripten Authors. All rights reserved.
2
// Emscripten is available under two separate licenses, the MIT license and the
3
// University of Illinois/NCSA Open Source License. Both these licenses can be
4
// found in the LICENSE file.
5
6
#include <SDL2/SDL.h>
7
#include <SDL2/SDL_audio.h>
8
#include <queue>
9
#include <cmath>
10
#include <stdio.h>
11
#include <assert.h>
12
13
#ifndef M_PI
14
#define M_PI 3.14159265358979323846f
15
#endif
16
17
#ifdef __EMSCRIPTEN__
18
#include "emscripten/emscripten.h"
19
#endif
20
21
#ifdef main
22
#undef main
23
#endif
24
25
const int tone_duration = 1000;
26
27
struct BeepObject {
28
double toneFrequency;
29
int samplesLeft;
30
};
31
32
class Beeper {
33
private:
34
double phase;
35
int frequency;
36
int numChannels;
37
int mutedChannel;
38
public:
39
Beeper(int frequency, int numChannels, int sdlAudioFormat);
40
~Beeper();
41
void beep(double toneFrequency, int durationMSecs);
42
template<typename T>
43
void generateSamples(T *stream, int length);
44
void wait();
45
46
std::queue<BeepObject> beeps;
47
int sdlAudioFormat;
48
};
49
50
void audio_callback(void*, Uint8*, int);
51
52
Beeper::Beeper(int frequency_, int numChannels_, int sdlAudioFormat_) {
53
phase = 0.0;
54
mutedChannel = 1;
55
56
SDL_AudioSpec desiredSpec;
57
58
desiredSpec.freq = frequency_;
59
desiredSpec.format = sdlAudioFormat_;
60
desiredSpec.channels = numChannels_;
61
desiredSpec.samples = 1024; // This is samples per channel.
62
desiredSpec.callback = audio_callback;
63
desiredSpec.userdata = this;
64
65
SDL_AudioSpec obtainedSpec;
66
67
// you might want to look for errors here
68
SDL_OpenAudio(&desiredSpec, &obtainedSpec);
69
70
// In this test, we require *exactly* the identical SDL result that we provide, since we test
71
// all various configurations individually.
72
if (obtainedSpec.freq != desiredSpec.freq || obtainedSpec.format != desiredSpec.format
73
|| obtainedSpec.channels != desiredSpec.channels || obtainedSpec.samples != desiredSpec.samples) {
74
SDL_CloseAudio();
75
throw std::runtime_error("Failed to initialize desired SDL_OpenAudio!");
76
}
77
78
frequency = obtainedSpec.freq;
79
numChannels = obtainedSpec.channels;
80
sdlAudioFormat = obtainedSpec.format;
81
82
// Immediately start producing audio.
83
SDL_PauseAudio(0);
84
}
85
86
Beeper::~Beeper() {
87
SDL_CloseAudio();
88
}
89
90
template<typename T>
91
void Beeper::generateSamples(T *stream, int length) {
92
const int AMPLITUDE = (sizeof(T) == 2) ? 28000 : 120;
93
const int offset = (sdlAudioFormat == AUDIO_U8) ? 120 : 0;
94
95
int i = 0;
96
length /= numChannels;
97
while (i < length) {
98
if (beeps.empty()) {
99
memset(stream + numChannels*i, 0, sizeof(T)*numChannels*(length-i));
100
return;
101
}
102
BeepObject& bo = beeps.front();
103
104
// In Stereo tests, mute one of the channels to be able to distinguish that Stereo output works.
105
if (bo.samplesLeft > tone_duration * frequency / 2 / 1000) {
106
mutedChannel = 1;
107
} else {
108
mutedChannel = 0;
109
}
110
111
int samplesToDo = std::min(i + bo.samplesLeft, length);
112
bo.samplesLeft -= samplesToDo - i;
113
114
while (i < samplesToDo) {
115
for(int j = 0; j < numChannels; ++j) {
116
stream[numChannels*i+j] = (T)(offset + (int)(AMPLITUDE * std::sin(phase * 2 * M_PI / frequency)));
117
if (numChannels > 1 && j == mutedChannel) {
118
stream[numChannels*i+j] = 0;
119
}
120
}
121
phase += bo.toneFrequency;
122
i++;
123
}
124
125
if (bo.samplesLeft == 0) {
126
beeps.pop();
127
}
128
}
129
}
130
131
void Beeper::beep(double toneFrequency, int durationMSecs) {
132
BeepObject bo;
133
bo.toneFrequency = toneFrequency;
134
bo.samplesLeft = durationMSecs * frequency / 1000;
135
136
SDL_LockAudio();
137
beeps.push(bo);
138
SDL_UnlockAudio();
139
}
140
141
Beeper *beep = 0;
142
143
// Test all kinds of various possible formats. Not all are supported, but running this
144
// test will report you which work.
145
const int freqs[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 96000 };
146
const int channels[] = { 1, 2 };
147
const int sdlAudioFormats[] = { AUDIO_U8, AUDIO_S16LSB /*, AUDIO_S8, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16MSB */ };
148
149
const char *SdlAudioFormatToString(int sdlAudioType) {
150
switch(sdlAudioType) {
151
case AUDIO_U8: return "AUDIO_U8";
152
case AUDIO_S8: return "AUDIO_S8";
153
case AUDIO_U16LSB: return "AUDIO_U16LSB";
154
case AUDIO_U16MSB: return "AUDIO_U16MSB";
155
case AUDIO_S16LSB: return "AUDIO_S16LSB";
156
case AUDIO_S16MSB: return "AUDIO_S16MSB";
157
default: return "(unknown)";
158
}
159
}
160
161
#define NUM_ELEMS(x) (sizeof(x)/sizeof((x)[0]))
162
163
// Indices to the currently running test.
164
int f = -1;
165
int c = 0;
166
int s = 0;
167
168
void nextTest(void *unused = 0) {
169
++f;
170
if (f >= NUM_ELEMS(freqs)) {
171
f = 0;
172
++c;
173
if (c >= NUM_ELEMS(channels)) {
174
c = 0;
175
++s;
176
if (s >= NUM_ELEMS(sdlAudioFormats)) {
177
printf("All tests done. Quit.\n");
178
#ifdef __EMSCRIPTEN__
179
emscripten_cancel_main_loop();
180
#endif
181
return;
182
}
183
}
184
}
185
186
double Hz = 440;
187
try {
188
beep = new Beeper(freqs[f], channels[c], sdlAudioFormats[s]);
189
} catch(...) {
190
printf("FAILED to play beep for %d msecs at %d Hz tone with audio format %s, %d channels, and %d samples/sec.\n",
191
tone_duration, (int)Hz, SdlAudioFormatToString(sdlAudioFormats[s]), channels[c], freqs[f]);
192
nextTest();
193
return;
194
}
195
196
printf("Playing back a beep for %d msecs at %d Hz tone with audio format %s, %d channels, and %d samples/sec.\n",
197
tone_duration, (int)Hz, SdlAudioFormatToString(sdlAudioFormats[s]), channels[c], freqs[f]);
198
beep->beep(Hz, tone_duration);
199
}
200
201
void update() {
202
SDL_LockAudio();
203
int size = beep->beeps.size();
204
SDL_UnlockAudio();
205
if (size == 0 && beep) {
206
delete beep;
207
beep = 0;
208
#ifdef __EMSCRIPTEN__
209
emscripten_async_call(nextTest, 0, 1500);
210
#else
211
SDL_Delay(1500);
212
nextTest();
213
#endif
214
}
215
}
216
217
void audio_callback(void *_beeper, Uint8 *_stream, int _length) {
218
Beeper* beeper = (Beeper*) _beeper;
219
220
if (beeper->sdlAudioFormat == AUDIO_U8) {
221
Uint8 *stream = (Uint8*) _stream;
222
beeper->generateSamples(stream, _length);
223
} else if (beeper->sdlAudioFormat == AUDIO_S16LSB) {
224
Sint16 *stream = (Sint16*) _stream;
225
int length = _length / 2;
226
beeper->generateSamples(stream, length);
227
} else {
228
assert(false && "Audio sample generation not implemented for current format!\n");
229
}
230
}
231
232
int main(int argc, char** argv) {
233
SDL_Init(SDL_INIT_AUDIO);
234
235
nextTest();
236
237
if (!beep)
238
return 1;
239
240
#ifdef __EMSCRIPTEN__
241
emscripten_set_main_loop(update, 60, 0);
242
#else
243
while(beep) {
244
SDL_Delay(20);
245
update();
246
}
247
#endif
248
249
return 0;
250
}
251
252