aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt19
-rw-r--r--examples/altonegen.c250
2 files changed, 269 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index af9e961a..822649ef 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -34,6 +34,7 @@ OPTION(ALSOFT_UTILS "Build and install utility programs" ON)
OPTION(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF)
OPTION(ALSOFT_EXAMPLES "Build and install example programs" ON)
+OPTION(ALSOFT_TESTS "Build and install test programs" ON)
OPTION(ALSOFT_CONFIG "Install alsoft.conf sample configuration file" ON)
OPTION(ALSOFT_HRTF_DEFS "Install HRTF definition files" ON)
@@ -1226,6 +1227,24 @@ IF(ALSOFT_UTILS)
MESSAGE(STATUS "")
ENDIF()
+IF(ALSOFT_TESTS)
+ ADD_LIBRARY(test-common STATIC examples/common/alhelpers.c)
+
+ ADD_EXECUTABLE(altonegen examples/altonegen.c)
+ TARGET_LINK_LIBRARIES(altonegen test-common ${LIBNAME})
+
+ IF(ALSOFT_INSTALL)
+ INSTALL(TARGETS altonegen
+ RUNTIME DESTINATION bin
+ LIBRARY DESTINATION "lib${LIB_SUFFIX}"
+ ARCHIVE DESTINATION "lib${LIB_SUFFIX}"
+ )
+ ENDIF()
+
+ MESSAGE(STATUS "Building test programs")
+ MESSAGE(STATUS "")
+ENDIF()
+
IF(ALSOFT_EXAMPLES)
IF(SDL2_FOUND AND SDL_SOUND_FOUND)
ADD_LIBRARY(ex-common STATIC examples/common/alhelpers.c
diff --git a/examples/altonegen.c b/examples/altonegen.c
new file mode 100644
index 00000000..d8601f7e
--- /dev/null
+++ b/examples/altonegen.c
@@ -0,0 +1,250 @@
+/*
+ * OpenAL Tone Generator Test
+ *
+ * Copyright (c) 2015 by Chris Robinson <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* This file contains a test for generating waveforms and plays them for a
+ * given length of time. Intended to inspect the behavior of the mixer by
+ * checking the output with a spectrum analyzer and oscilloscope.
+ *
+ * TODO: This would actually be nicer as a GUI app with buttons to start and
+ * stop individual waveforms, include additional whitenoise and pinknoise
+ * generators, and have the ability to hook up EFX filters and effects.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/alext.h"
+
+#include "common/alhelpers.h"
+
+#ifndef M_PI
+#define M_PI (3.14159265358979323846)
+#endif
+
+enum WaveType {
+ WT_Sine,
+ WT_Square,
+ WT_Sawtooth,
+ WT_Triangle,
+};
+
+static void ApplySin(ALfloat *data, ALdouble g, ALuint srate, ALuint freq)
+{
+ ALdouble smps_per_cycle = (ALdouble)srate / freq;
+ ALuint i;
+ for(i = 0;i < srate;i++)
+ data[i] += sin(i/smps_per_cycle * 2.0*M_PI) * g;
+}
+
+/* Generates waveforms using additive synthesis. Each waveform is constructed
+ * by summing one or more sine waves, up to (and excluding) nyquist.
+ */
+static ALuint CreateWave(enum WaveType type, ALuint freq, ALuint srate)
+{
+ ALint data_size;
+ ALfloat *data;
+ ALuint buffer;
+ ALenum err;
+ ALuint i;
+
+ data_size = srate * sizeof(ALfloat);
+ data = calloc(1, data_size);
+ if(type == WT_Sine)
+ ApplySin(data, 1.0, srate, freq);
+ else if(type == WT_Square)
+ for(i = 1;freq*i < srate/2;i+=2)
+ ApplySin(data, 4.0/M_PI * 1.0/i, srate, freq*i);
+ else if(type == WT_Sawtooth)
+ for(i = 1;freq*i < srate/2;i++)
+ ApplySin(data, 2.0/M_PI * ((i&1)*2 - 1.0) / i, srate, freq*i);
+ else if(type == WT_Triangle)
+ for(i = 1;freq*i < srate/2;i+=2)
+ ApplySin(data, 8.0/(M_PI*M_PI) * (1.0 - (i&2)) / (i*i), srate, freq*i);
+
+ /* Buffer the audio data into a new buffer object. */
+ buffer = 0;
+ alGenBuffers(1, &buffer);
+ alBufferData(buffer, AL_FORMAT_MONO_FLOAT32, data, data_size, srate);
+ free(data);
+
+ /* Check if an error occured, and clean up if so. */
+ err = alGetError();
+ if(err != AL_NO_ERROR)
+ {
+ fprintf(stderr, "OpenAL Error: %s\n", alGetString(err));
+ if(alIsBuffer(buffer))
+ alDeleteBuffers(1, &buffer);
+ return 0;
+ }
+
+ return buffer;
+}
+
+
+int main(int argc, char *argv[])
+{
+ enum WaveType wavetype = WT_Sine;
+ ALuint source, buffer;
+ ALint last_pos, num_loops;
+ ALint max_loops = 4;
+ ALint srate = 44100;
+ ALint tone_freq = 1000;
+ ALenum state;
+ int i;
+
+ for(i = 1;i < argc;i++)
+ {
+ if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
+ {
+ fprintf(stderr, "OpenAL Tone Generator\n"
+"\n"
+"Usage: %s <options>\n"
+"\n"
+"Available options:\n"
+" --help/-h This help text\n"
+" -t <seconds> Time to play a tone (default 5 seconds)\n"
+" --waveform/-w <type> Waveform type: sine (default), square, sawtooth,\n"
+" triangle\n"
+" --freq/-f <hz> Tone frequency (default 1000 hz)\n"
+" --srate/-s <sample rate> Sampling rate (default 44100 hz)\n",
+ argv[0]
+ );
+ return 1;
+ }
+ else if(i+1 < argc && strcmp(argv[i], "-t") == 0)
+ {
+ i++;
+ max_loops = atoi(argv[i]) - 1;
+ }
+ else if(i+1 < argc && (strcmp(argv[i], "--waveform") == 0 || strcmp(argv[i], "-w") == 0))
+ {
+ i++;
+ if(strcmp(argv[i], "sine") == 0)
+ wavetype = WT_Sine;
+ else if(strcmp(argv[i], "square") == 0)
+ wavetype = WT_Square;
+ else if(strcmp(argv[i], "sawtooth") == 0)
+ wavetype = WT_Sawtooth;
+ else if(strcmp(argv[i], "triangle") == 0)
+ wavetype = WT_Triangle;
+ else
+ fprintf(stderr, "Unhandled waveform: %s\n", argv[i]);
+ }
+ else if(i+1 < argc && (strcmp(argv[i], "--freq") == 0 || strcmp(argv[i], "-f") == 0))
+ {
+ i++;
+ tone_freq = atoi(argv[i]);
+ if(tone_freq < 20)
+ {
+ fprintf(stderr, "Invalid tone frequency: %s (min: 20hz)\n", argv[i]);
+ tone_freq = 20;
+ }
+ }
+ else if(i+1 < argc && (strcmp(argv[i], "--srate") == 0 || strcmp(argv[i], "-s") == 0))
+ {
+ i++;
+ srate = atoi(argv[i]);
+ if(srate < 40)
+ {
+ fprintf(stderr, "Invalid sample rate: %s (min: 40hz)\n", argv[i]);
+ srate = 40;
+ }
+ }
+ }
+
+ InitAL();
+
+ if(!alIsExtensionPresent("AL_EXT_FLOAT32"))
+ {
+ fprintf(stderr, "Required AL_EXT_FLOAT32 extension not supported on this device!\n");
+ CloseAL();
+ return 1;
+ }
+
+ /* Load the sound into a buffer. */
+ buffer = CreateWave(wavetype, tone_freq, srate);
+ if(!buffer)
+ {
+ CloseAL();
+ return 1;
+ }
+
+ {
+ ALCdevice *device;
+ ALCint dev_rate;
+ ALint buf_rate;
+
+ device = alcGetContextsDevice(alcGetCurrentContext());
+ alcGetIntegerv(device, ALC_FREQUENCY, 1, &dev_rate);
+ assert(alcGetError(device)==ALC_NO_ERROR && "Failed to get device sample rate");
+
+ alGetBufferi(buffer, AL_FREQUENCY, &buf_rate);
+ assert(alGetError()==AL_NO_ERROR && "Failed to get buffer sample rate");
+
+ printf("Playing %dhz tone with %dhz sample rate and %dhz output, for %d second%s...\n",
+ tone_freq, buf_rate, dev_rate, max_loops+1, max_loops?"s":"");
+ fflush(stdout);
+ }
+
+ /* Create the source to play the sound with. */
+ source = 0;
+ alGenSources(1, &source);
+ alSourcei(source, AL_BUFFER, buffer);
+ assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
+
+ /* Play the sound for a while. */
+ num_loops = 0;
+ last_pos = 0;
+ alSourcei(source, AL_LOOPING, (max_loops > 0) ? AL_TRUE : AL_FALSE);
+ alSourcePlay(source);
+ do {
+ ALint pos;
+ Sleep(10);
+ alGetSourcei(source, AL_SAMPLE_OFFSET, &pos);
+ alGetSourcei(source, AL_SOURCE_STATE, &state);
+ if(pos < last_pos && state == AL_PLAYING)
+ {
+ ++num_loops;
+ if(num_loops >= max_loops)
+ alSourcei(source, AL_LOOPING, AL_FALSE);
+ printf("%d...\n", max_loops - num_loops + 1);
+ fflush(stdout);
+ }
+ last_pos = pos;
+ } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING);
+
+ /* All done. Delete resources, and close OpenAL. */
+ alDeleteSources(1, &source);
+ alDeleteBuffers(1, &buffer);
+
+ /* Close up OpenAL. */
+ CloseAL();
+
+ return 0;
+}