aboutsummaryrefslogtreecommitdiffstats
path: root/examples/altonegen.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/altonegen.c')
-rw-r--r--examples/altonegen.c271
1 files changed, 271 insertions, 0 deletions
diff --git a/examples/altonegen.c b/examples/altonegen.c
new file mode 100644
index 00000000..65980529
--- /dev/null
+++ b/examples/altonegen.c
@@ -0,0 +1,271 @@
+/*
+ * 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,
+ WT_Impulse,
+};
+
+static const char *GetWaveTypeName(enum WaveType type)
+{
+ switch(type)
+ {
+ case WT_Sine: return "sine";
+ case WT_Square: return "square";
+ case WT_Sawtooth: return "sawtooth";
+ case WT_Triangle: return "triangle";
+ case WT_Impulse: return "impulse";
+ }
+ return "(unknown)";
+}
+
+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] += (ALfloat)(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);
+ else if(type == WT_Impulse)
+ {
+ /* NOTE: Impulse isn't really a waveform, but it can still be useful to
+ * test (other than resampling, the ALSOFT_DEFAULT_REVERB environment
+ * variable can prove useful here to test the reverb response).
+ */
+ for(i = 0;i < srate;i++)
+ data[i] = (i%(srate/freq)) ? 0.0f : 1.0f;
+ }
+
+ /* 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 = -1;
+ ALint tone_freq = 1000;
+ ALCint dev_rate;
+ 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, impulse\n"
+" --freq/-f <hz> Tone frequency (default 1000 hz)\n"
+" --srate/-s <sample rate> Sampling rate (default output rate)\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 if(strcmp(argv[i], "impulse") == 0)
+ wavetype = WT_Impulse;
+ 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 < 1)
+ {
+ fprintf(stderr, "Invalid tone frequency: %s (min: 1hz)\n", argv[i]);
+ tone_freq = 1;
+ }
+ }
+ 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;
+ }
+
+ {
+ ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
+ alcGetIntegerv(device, ALC_FREQUENCY, 1, &dev_rate);
+ assert(alcGetError(device)==ALC_NO_ERROR && "Failed to get device sample rate");
+ }
+ if(srate < 0)
+ srate = dev_rate;
+
+ /* Load the sound into a buffer. */
+ buffer = CreateWave(wavetype, tone_freq, srate);
+ if(!buffer)
+ {
+ CloseAL();
+ return 1;
+ }
+
+ printf("Playing %dhz %s-wave tone with %dhz sample rate and %dhz output, for %d second%s...\n",
+ tone_freq, GetWaveTypeName(wavetype), srate, 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;
+}