aboutsummaryrefslogtreecommitdiff
path: root/tests/sdl_audio_beep.cpp
diff options
context:
space:
mode:
authorJukka Jylänki <jujjyl@gmail.com>2013-06-16 14:27:35 +0300
committerAlon Zakai <alonzakai@gmail.com>2013-09-24 16:56:44 -0700
commit7a1e760e82ccef4bc950890bb11fd1a59e88f11a (patch)
tree2d031eac943b0224cf7eb339e353647f749c847b /tests/sdl_audio_beep.cpp
parent6df56c0c5188f2316f75c414ccea9c3f735904ba (diff)
Improve SDL_OpenAudio support to work with the newest Web Audio API spec, add better support for different SDL audio formats and sample rates. Add browser test for SDL audio beep sample.
Diffstat (limited to 'tests/sdl_audio_beep.cpp')
-rw-r--r--tests/sdl_audio_beep.cpp292
1 files changed, 204 insertions, 88 deletions
diff --git a/tests/sdl_audio_beep.cpp b/tests/sdl_audio_beep.cpp
index 4bcc3b85..afe6a610 100644
--- a/tests/sdl_audio_beep.cpp
+++ b/tests/sdl_audio_beep.cpp
@@ -2,127 +2,243 @@
#include <SDL/SDL_audio.h>
#include <queue>
#include <cmath>
+#include <stdio.h>
+#include <assert.h>
-const int AMPLITUDE = 28000;
-const int FREQUENCY = 44100;
+#ifndef M_PI
+#define M_PI 3.14159265358979323846f
+#endif
-struct BeepObject
-{
- double freq;
- int samplesLeft;
+#ifdef EMSCRIPTEN
+#include "emscripten/emscripten.h"
+#endif
+
+#ifdef main
+#undef main
+#endif
+
+const int tone_duration = 2000;
+
+struct BeepObject {
+ double toneFrequency;
+ int samplesLeft;
};
-class Beeper
-{
+class Beeper {
private:
- double v;
- std::queue<BeepObject> beeps;
+ double phase;
+ int frequency;
+ int numChannels;
+ int mutedChannel;
public:
- Beeper();
- ~Beeper();
- void beep(double freq, int duration);
- void generateSamples(Sint16 *stream, int length);
- void wait();
+ Beeper(int frequency, int numChannels, int sdlAudioFormat);
+ ~Beeper();
+ void beep(double toneFrequency, int durationMSecs);
+ template<typename T>
+ void generateSamples(T *stream, int length);
+ void wait();
+
+ std::queue<BeepObject> beeps;
+ int sdlAudioFormat;
};
void audio_callback(void*, Uint8*, int);
-Beeper::Beeper()
-{
- SDL_AudioSpec desiredSpec;
+Beeper::Beeper(int frequency_, int numChannels_, int sdlAudioFormat_) {
+ phase = 0.0;
+ mutedChannel = 1;
- desiredSpec.freq = FREQUENCY;
- desiredSpec.format = AUDIO_S16SYS;
- desiredSpec.channels = 1;
- desiredSpec.samples = 2048;
- desiredSpec.callback = audio_callback;
- desiredSpec.userdata = this;
+ SDL_AudioSpec desiredSpec;
- SDL_AudioSpec obtainedSpec;
+ desiredSpec.freq = frequency_;
+ desiredSpec.format = sdlAudioFormat_;
+ desiredSpec.channels = numChannels_;
+ desiredSpec.samples = 1024; // This is samples per channel.
+ desiredSpec.callback = audio_callback;
+ desiredSpec.userdata = this;
- // you might want to look for errors here
- SDL_OpenAudio(&desiredSpec, &obtainedSpec);
+ SDL_AudioSpec obtainedSpec;
- // start play audio
- SDL_PauseAudio(0);
-}
+ // you might want to look for errors here
+ SDL_OpenAudio(&desiredSpec, &obtainedSpec);
-Beeper::~Beeper()
-{
+ // In this test, we require *exactly* the identical SDL result that we provide, since we test
+ // all various configurations individually.
+ if (obtainedSpec.freq != desiredSpec.freq || obtainedSpec.format != desiredSpec.format
+ || obtainedSpec.channels != desiredSpec.channels || obtainedSpec.samples != desiredSpec.samples) {
SDL_CloseAudio();
+ throw std::runtime_error("Failed to initialize desired SDL_OpenAudio!");
+ }
+
+ frequency = obtainedSpec.freq;
+ numChannels = obtainedSpec.channels;
+ sdlAudioFormat = obtainedSpec.format;
+
+ // Immediately start producing audio.
+ SDL_PauseAudio(0);
}
-void Beeper::generateSamples(Sint16 *stream, int length)
-{
- int i = 0;
- while (i < length) {
-
- if (beeps.empty()) {
- while (i < length) {
- stream[i] = 0;
- i++;
- }
- return;
- }
- BeepObject& bo = beeps.front();
+Beeper::~Beeper() {
+ SDL_CloseAudio();
+}
+
+template<typename T>
+void Beeper::generateSamples(T *stream, int length) {
+ const int AMPLITUDE = (sizeof(T) == 2) ? 28000 : 120;
+ const int offset = (sdlAudioFormat == AUDIO_U8) ? 120 : 0;
+
+ int i = 0;
+ length /= numChannels;
+ while (i < length) {
+ if (beeps.empty()) {
+ memset(stream + numChannels*i, 0, sizeof(T)*numChannels*(length-i));
+ return;
+ }
+ BeepObject& bo = beeps.front();
- int samplesToDo = std::min(i + bo.samplesLeft, length);
- bo.samplesLeft -= samplesToDo - i;
+ // In Stereo tests, mute one of the channels to be able to distinguish that Stereo output works.
+ if (bo.samplesLeft > tone_duration * frequency / 2 / 1000) {
+ mutedChannel = 1;
+ } else {
+ mutedChannel = 0;
+ }
- while (i < samplesToDo) {
- stream[i] = AMPLITUDE * std::sin(v * 2 * M_PI / FREQUENCY);
- i++;
- v += bo.freq;
- }
+ int samplesToDo = std::min(i + bo.samplesLeft, length);
+ bo.samplesLeft -= samplesToDo - i;
- if (bo.samplesLeft == 0) {
- beeps.pop();
+ while (i < samplesToDo) {
+ for(int j = 0; j < numChannels; ++j) {
+ stream[numChannels*i+j] = (T)(offset + (int)(AMPLITUDE * std::sin(phase * 2 * M_PI / frequency)));
+ if (numChannels > 1 && j == mutedChannel) {
+ stream[numChannels*i+j] = 0;
}
+ }
+ phase += bo.toneFrequency;
+ i++;
}
+
+ if (bo.samplesLeft == 0) {
+ beeps.pop();
+ }
+ }
}
-void Beeper::beep(double freq, int duration)
-{
- BeepObject bo;
- bo.freq = freq;
- bo.samplesLeft = duration * FREQUENCY / 1000;
+void Beeper::beep(double toneFrequency, int durationMSecs) {
+ BeepObject bo;
+ bo.toneFrequency = toneFrequency;
+ bo.samplesLeft = durationMSecs * frequency / 1000;
+
+ SDL_LockAudio();
+ beeps.push(bo);
+ SDL_UnlockAudio();
+}
- SDL_LockAudio();
- beeps.push(bo);
- SDL_UnlockAudio();
+Beeper *beep = 0;
+
+// Test all kinds of various possible formats. Not all are supported, but running this
+// test will report you which work.
+const int freqs[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 96000 };
+const int channels[] = { 1, 2 };
+const int sdlAudioFormats[] = { AUDIO_U8, AUDIO_S16LSB /*, AUDIO_S8, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16MSB */ };
+
+const char *SdlAudioFormatToString(int sdlAudioType) {
+ switch(sdlAudioType) {
+ case AUDIO_U8: return "AUDIO_U8";
+ case AUDIO_S8: return "AUDIO_S8";
+ case AUDIO_U16LSB: return "AUDIO_U16LSB";
+ case AUDIO_U16MSB: return "AUDIO_U16MSB";
+ case AUDIO_S16LSB: return "AUDIO_S16LSB";
+ case AUDIO_S16MSB: return "AUDIO_S16MSB";
+ default: return "(unknown)";
+ }
}
-void Beeper::wait()
-{
- int size;
- do {
- SDL_Delay(20);
- SDL_LockAudio();
- size = beeps.size();
- SDL_UnlockAudio();
- } while (size > 0);
+#define NUM_ELEMS(x) (sizeof(x)/sizeof((x)[0]))
+
+// Indices to the currently running test.
+int f = -1;
+int c = 0;
+int s = 0;
+
+void nextTest(void *unused = 0) {
+ ++f;
+ if (f >= NUM_ELEMS(freqs)) {
+ f = 0;
+ ++c;
+ if (c >= NUM_ELEMS(channels)) {
+ c = 0;
+ ++s;
+ if (s >= NUM_ELEMS(sdlAudioFormats)) {
+ printf("All tests done. Quit.\n");
+#ifdef EMSCRIPTEN
+ emscripten_cancel_main_loop();
+ int result = 1;
+ REPORT_RESULT();
+#endif
+ return;
+ }
+ }
+ }
+
+ double Hz = 440;
+ try {
+ beep = new Beeper(freqs[f], channels[c], sdlAudioFormats[s]);
+ } catch(...) {
+ printf("FAILED to play beep for %d msecs at %d Hz tone with audio format %s, %d channels, and %d samples/sec.\n",
+ tone_duration, (int)Hz, SdlAudioFormatToString(sdlAudioFormats[s]), channels[c], freqs[f]);
+ nextTest();
+ return;
+ }
+
+ printf("Playing back a beep for %d msecs at %d Hz tone with audio format %s, %d channels, and %d samples/sec.\n",
+ tone_duration, (int)Hz, SdlAudioFormatToString(sdlAudioFormats[s]), channels[c], freqs[f]);
+ beep->beep(Hz, tone_duration);
+}
+void update() {
+ SDL_LockAudio();
+ int size = beep->beeps.size();
+ SDL_UnlockAudio();
+ if (size == 0 && beep) {
+ delete beep;
+ beep = 0;
+#ifdef EMSCRIPTEN
+ emscripten_async_call(nextTest, 0, 500);
+#else
+ SDL_Delay(500);
+ nextTest();
+#endif
+ }
}
-void audio_callback(void *_beeper, Uint8 *_stream, int _length)
-{
+void audio_callback(void *_beeper, Uint8 *_stream, int _length) {
+ Beeper* beeper = (Beeper*) _beeper;
+
+ if (beeper->sdlAudioFormat == AUDIO_U8) {
+ Uint8 *stream = (Uint8*) _stream;
+ beeper->generateSamples(stream, _length);
+ } else if (beeper->sdlAudioFormat == AUDIO_S16LSB) {
Sint16 *stream = (Sint16*) _stream;
int length = _length / 2;
- Beeper* beeper = (Beeper*) _beeper;
-
beeper->generateSamples(stream, length);
+ } else {
+ assert(false && "Audio sample generation not implemented for current format!\n");
+ }
}
-int main(int argc, char* argv[])
-{
- SDL_Init(SDL_INIT_AUDIO);
-
- int duration = 1000;
- double Hz = 440;
-
- Beeper b;
- b.beep(Hz, duration);
- b.wait();
-
- return 0;
-} \ No newline at end of file
+int main(int argc, char** argv) {
+ SDL_Init(SDL_INIT_AUDIO);
+
+ nextTest();
+
+#ifdef EMSCRIPTEN
+ emscripten_set_main_loop(update, 60, 0);
+#else
+ while(beep) {
+ SDL_Delay(20);
+ update();
+ }
+#endif
+
+ return 0;
+}