Path: blob/main/test/browser/test_sdl2_audio_beep.cpp
6162 views
// Copyright 2014 The Emscripten Authors. All rights reserved.1// Emscripten is available under two separate licenses, the MIT license and the2// University of Illinois/NCSA Open Source License. Both these licenses can be3// found in the LICENSE file.45#include <SDL2/SDL.h>6#include <SDL2/SDL_audio.h>7#include <queue>8#include <cmath>9#include <stdio.h>10#include <assert.h>1112#ifndef M_PI13#define M_PI 3.14159265358979323846f14#endif1516#ifdef __EMSCRIPTEN__17#include "emscripten/emscripten.h"18#endif1920#ifdef main21#undef main22#endif2324const int tone_duration = 1000;2526struct BeepObject {27double toneFrequency;28int samplesLeft;29};3031class Beeper {32private:33double phase;34int frequency;35int numChannels;36int mutedChannel;37public:38Beeper(int frequency, int numChannels, int sdlAudioFormat);39~Beeper();40void beep(double toneFrequency, int durationMSecs);41template<typename T>42void generateSamples(T *stream, int length);43void wait();4445std::queue<BeepObject> beeps;46int sdlAudioFormat;47};4849void audio_callback(void*, Uint8*, int);5051Beeper::Beeper(int frequency_, int numChannels_, int sdlAudioFormat_) {52phase = 0.0;53mutedChannel = 1;5455SDL_AudioSpec desiredSpec;5657desiredSpec.freq = frequency_;58desiredSpec.format = sdlAudioFormat_;59desiredSpec.channels = numChannels_;60desiredSpec.samples = 1024; // This is samples per channel.61desiredSpec.callback = audio_callback;62desiredSpec.userdata = this;6364SDL_AudioSpec obtainedSpec;6566// you might want to look for errors here67SDL_OpenAudio(&desiredSpec, &obtainedSpec);6869// In this test, we require *exactly* the identical SDL result that we provide, since we test70// all various configurations individually.71if (obtainedSpec.freq != desiredSpec.freq || obtainedSpec.format != desiredSpec.format72|| obtainedSpec.channels != desiredSpec.channels || obtainedSpec.samples != desiredSpec.samples) {73SDL_CloseAudio();74throw std::runtime_error("Failed to initialize desired SDL_OpenAudio!");75}7677frequency = obtainedSpec.freq;78numChannels = obtainedSpec.channels;79sdlAudioFormat = obtainedSpec.format;8081// Immediately start producing audio.82SDL_PauseAudio(0);83}8485Beeper::~Beeper() {86SDL_CloseAudio();87}8889template<typename T>90void Beeper::generateSamples(T *stream, int length) {91const int AMPLITUDE = (sizeof(T) == 2) ? 28000 : 120;92const int offset = (sdlAudioFormat == AUDIO_U8) ? 120 : 0;9394int i = 0;95length /= numChannels;96while (i < length) {97if (beeps.empty()) {98memset(stream + numChannels*i, 0, sizeof(T)*numChannels*(length-i));99return;100}101BeepObject& bo = beeps.front();102103// In Stereo tests, mute one of the channels to be able to distinguish that Stereo output works.104if (bo.samplesLeft > tone_duration * frequency / 2 / 1000) {105mutedChannel = 1;106} else {107mutedChannel = 0;108}109110int samplesToDo = std::min(i + bo.samplesLeft, length);111bo.samplesLeft -= samplesToDo - i;112113while (i < samplesToDo) {114for(int j = 0; j < numChannels; ++j) {115stream[numChannels*i+j] = (T)(offset + (int)(AMPLITUDE * std::sin(phase * 2 * M_PI / frequency)));116if (numChannels > 1 && j == mutedChannel) {117stream[numChannels*i+j] = 0;118}119}120phase += bo.toneFrequency;121i++;122}123124if (bo.samplesLeft == 0) {125beeps.pop();126}127}128}129130void Beeper::beep(double toneFrequency, int durationMSecs) {131BeepObject bo;132bo.toneFrequency = toneFrequency;133bo.samplesLeft = durationMSecs * frequency / 1000;134135SDL_LockAudio();136beeps.push(bo);137SDL_UnlockAudio();138}139140Beeper *beep = 0;141142// Test all kinds of various possible formats. Not all are supported, but running this143// test will report you which work.144const int freqs[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 96000 };145const int channels[] = { 1, 2 };146const int sdlAudioFormats[] = { AUDIO_U8, AUDIO_S16LSB /*, AUDIO_S8, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16MSB */ };147148const char *SdlAudioFormatToString(int sdlAudioType) {149switch(sdlAudioType) {150case AUDIO_U8: return "AUDIO_U8";151case AUDIO_S8: return "AUDIO_S8";152case AUDIO_U16LSB: return "AUDIO_U16LSB";153case AUDIO_U16MSB: return "AUDIO_U16MSB";154case AUDIO_S16LSB: return "AUDIO_S16LSB";155case AUDIO_S16MSB: return "AUDIO_S16MSB";156default: return "(unknown)";157}158}159160#define NUM_ELEMS(x) (sizeof(x)/sizeof((x)[0]))161162// Indices to the currently running test.163int f = -1;164int c = 0;165int s = 0;166167void nextTest(void *unused = 0) {168++f;169if (f >= NUM_ELEMS(freqs)) {170f = 0;171++c;172if (c >= NUM_ELEMS(channels)) {173c = 0;174++s;175if (s >= NUM_ELEMS(sdlAudioFormats)) {176printf("All tests done. Quit.\n");177#ifdef __EMSCRIPTEN__178emscripten_cancel_main_loop();179#endif180return;181}182}183}184185double Hz = 440;186try {187beep = new Beeper(freqs[f], channels[c], sdlAudioFormats[s]);188} catch(...) {189printf("FAILED to play beep for %d msecs at %d Hz tone with audio format %s, %d channels, and %d samples/sec.\n",190tone_duration, (int)Hz, SdlAudioFormatToString(sdlAudioFormats[s]), channels[c], freqs[f]);191nextTest();192return;193}194195printf("Playing back a beep for %d msecs at %d Hz tone with audio format %s, %d channels, and %d samples/sec.\n",196tone_duration, (int)Hz, SdlAudioFormatToString(sdlAudioFormats[s]), channels[c], freqs[f]);197beep->beep(Hz, tone_duration);198}199200void update() {201SDL_LockAudio();202int size = beep->beeps.size();203SDL_UnlockAudio();204if (size == 0 && beep) {205delete beep;206beep = 0;207#ifdef __EMSCRIPTEN__208emscripten_async_call(nextTest, 0, 1500);209#else210SDL_Delay(1500);211nextTest();212#endif213}214}215216void audio_callback(void *_beeper, Uint8 *_stream, int _length) {217Beeper* beeper = (Beeper*) _beeper;218219if (beeper->sdlAudioFormat == AUDIO_U8) {220Uint8 *stream = (Uint8*) _stream;221beeper->generateSamples(stream, _length);222} else if (beeper->sdlAudioFormat == AUDIO_S16LSB) {223Sint16 *stream = (Sint16*) _stream;224int length = _length / 2;225beeper->generateSamples(stream, length);226} else {227assert(false && "Audio sample generation not implemented for current format!\n");228}229}230231int main(int argc, char** argv) {232SDL_Init(SDL_INIT_AUDIO);233234nextTest();235236if (!beep)237return 1;238239#ifdef __EMSCRIPTEN__240emscripten_set_main_loop(update, 60, 0);241#else242while(beep) {243SDL_Delay(20);244update();245}246#endif247248return 0;249}250251252