JOAL v2.6.0-rc-20250712
JOAL, OpenAL® API Binding for Java™ (public API).
Synth01AL.java
Go to the documentation of this file.
1/**
2 * Copyright 2023 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28package com.jogamp.openal.test.manual;
29
30import java.io.BufferedReader;
31import java.io.IOException;
32import java.io.InputStreamReader;
33import java.nio.ShortBuffer;
34
35import com.jogamp.openal.AL;
36import com.jogamp.openal.ALC;
37import com.jogamp.openal.ALCcontext;
38import com.jogamp.openal.ALCdevice;
39import com.jogamp.openal.ALConstants;
40import com.jogamp.openal.ALFactory;
41import com.jogamp.openal.ALVersion;
42
43/**
44 * A continuous simple on-thread immutable sine wave synthesizer.
45 * <p>
46 * Implementation simply finds the best loop'able sample-count for a fixed frequency
47 * and plays it indefinitely.
48 * </p>
49 */
50public class Synth01AL {
51 /** The value PI, i.e. 180 degrees in radians. */
52 public static final float PI = 3.14159265358979323846f;
53
54 /** The value 2PI, i.e. 360 degrees in radians. */
55 public static final float TWO_PI = 2f * PI;
56
57 private static final float EPSILON = 1.1920929E-7f; // Float.MIN_VALUE == 1.4e-45f ; double EPSILON 2.220446049250313E-16d
58
59 private static final float SHORT_MAX = 32767.0f; // == Short.MAX_VALUE
60
61 public static void waitForKey(final String message) {
62 final BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
63 System.err.println("> Press enter to "+message);
64 try {
65 System.err.println(stdin.readLine());
66 } catch (final IOException e) { e.printStackTrace(); }
67 }
68
69 private ALC alc = null;
70 private ALCdevice device = null;
71 private ALCcontext context = null;
72 private AL al = null;
73
74 private final int[] buffers = { 0 };
75 private final int[] sources = { 0 };
76
77 private void alCheckError(final String given_label, final boolean throwEx) {
78 final int error = al.alGetError();
79 if(ALConstants.AL_NO_ERROR != error) {
80 final String msg = String.format("ERROR - 0x%X %s (%s)", error, al.alGetString(error), given_label);
81 System.err.println(msg);
82 if( throwEx ) {
83 throw new RuntimeException(msg);
84 }
85 }
86 }
87
88 public void init() {
89 alc = ALFactory.getALC();
90 device = alc.alcOpenDevice(null);
91 context = alc.alcCreateContext(device, null);
92 alc.alcMakeContextCurrent(context);
93 al = ALFactory.getAL(); // valid after makeContextCurrent(..)
94 System.out.println("ALVersion: "+new ALVersion(al).toString());
95 System.out.println("Output devices:");
96 {
97 final String[] outDevices = alc.alcGetDeviceSpecifiers();
98 if( null != outDevices ) {
99 for (final String name : outDevices) {
100 System.out.println(" "+name);
101 }
102 }
103 }
104 System.out.println("Output all devices:");
105 {
106 final String[] outDevices = alc.alcGetAllDeviceSpecifiers();
107 if( null != outDevices ) {
108 for (final String name : outDevices) {
109 System.out.println(" "+name);
110 }
111 }
112 }
113 alCheckError("setup", true);
114
115 al.alGenBuffers(1, buffers, 0);
116 alCheckError("alGenBuffers", true);
117
118 // Set-up sound source
119 al.alGenSources(1, sources, 0);
120 alCheckError("alGenSources", true);
121 }
122
123 public void exit() {
124 // Stop the sources
125 al.alSourceStopv(1, sources, 0);
126 for (int ii = 0; ii < 1; ++ii) {
127 al.alSourcei(sources[ii], ALConstants.AL_BUFFER, 0);
128 }
129 alCheckError("sources: stop and disconnected", true);
130
131 // Clean-up
132 al.alDeleteSources(1, sources, 0);
133 al.alDeleteBuffers(1, buffers, 0);
134 alCheckError("sources/buffers: deleted", true);
135
136 if( null != context ) {
137 alc.alcMakeContextCurrent(null);
138 alc.alcDestroyContext(context);
139 context = null;
140 }
141 if( null != device ) {
142 alc.alcCloseDevice(device);
143 device = null;
144 }
145 }
146
147 public static int findBestWaveCount(final float freq, final int sampleRate, final int minWaves, final int maxWaves) {
148 final float period = 1.0f / freq; // [s]
149 final float sample_step = ( TWO_PI * freq ) / sampleRate;
150 int wave_count;
151 float s_diff = Float.MAX_VALUE;
152 float sc_diff = Float.MAX_VALUE;
153 int wc_best = -1;
154 for(wave_count = minWaves; wave_count < maxWaves && s_diff >= EPSILON; ++wave_count) {
155 final float d = wave_count * period; // duration [s] for 'i' full waves
156 final float sc_f = d * sampleRate; // sample count for 'i' full waves [n]
157 final int sc_i = (int)sc_f;
158 final float s1 = (float) Math.abs( Math.sin( sample_step * sc_i ) ); // last_step + 1 = next wave start == error to zero
159 if( s1 < s_diff ) {
160 s_diff = s1;
161 sc_diff = sc_f - sc_i; // sample_count delta float - int
162 wc_best = wave_count;
163 }
164 }
165 System.err.printf("%nBest: %d/[%d..%d], waves %d, sample_count diff %.12f, sample diff %.12f%n", wave_count, minWaves, maxWaves, wc_best, sc_diff, s_diff);
166 return wc_best;
167 }
168
169 private static final int SAMPLE_RATE = 44100; // [Hz]
170
171 public void loop(final float freq /* [Hz] */) {
172 final float period = 1.0f / freq; // [s]
173 final float sample_step = ( TWO_PI * freq ) / SAMPLE_RATE;
174
175 final int wave_count = findBestWaveCount(freq, SAMPLE_RATE, 10, 1000);
176 final float duration = wave_count * period; // [s], full waves
177 final int sample_count = (int)( duration * SAMPLE_RATE ); // [n]
178
179 System.err.printf("%nFreq %f Hz, period %f [ms], waves %d, duration %f [ms], sample[rate %d, step %f]%n", freq, 1000.0*period, wave_count, 1000.0*duration, SAMPLE_RATE, sample_step);
180
181 // allocate PCM audio buffer
182 final ShortBuffer samples = ShortBuffer.allocate(sample_count);
183
184 for(int i=0; i<sample_count; ++i) {
185 final float s = (float) Math.sin( sample_step * i );
186 samples.put( (short)( SHORT_MAX * s ) );
187 }
188 samples.rewind();
189 alCheckError("populating samples", true);
190
191 // upload buffer to OpenAL
192 al.alBufferData(buffers[0], ALConstants.AL_FORMAT_MONO16, samples, sample_count*2, SAMPLE_RATE);
193 alCheckError("alBufferData samples", true);
194
195 samples.clear();
196
197 // Play source / buffer
198 al.alSourcei(sources[0], ALConstants.AL_BUFFER, buffers[0]);
199 alCheckError("alSourcei source <-> buffer", true);
200
201 al.alSourcei(sources[0], ALConstants.AL_LOOPING, 1);
202 final int[] loopArray = new int[1];
203 al.alGetSourcei(sources[0], ALConstants.AL_LOOPING, loopArray, 0);
204 System.err.println("Looping 1: " + (loopArray[0] == ALConstants.AL_TRUE));
205
206 al.alSourceRewind(sources[0]);
207 al.alSourcePlay(sources[0]);
208 alCheckError("alSourcePlay", true);
209
210 // ---------------------
211
212 final int[] current_playing_state = { 0 };
213 al.alGetSourcei(sources[0], ALConstants.AL_SOURCE_STATE, current_playing_state, 0);
214 alCheckError("alGetSourcei AL_SOURCE_STATE", true);
215
216 if( ALConstants.AL_PLAYING == current_playing_state[0] ) {
217 waitForKey("Stop");
218 }
219 }
220
221 public static float atof(final String str, final float def) {
222 try {
223 return Float.parseFloat(str);
224 } catch (final Exception ex) {
225 ex.printStackTrace();
226 }
227 return def;
228 }
229
230 public static void main(final String[] args) {
231 float freq = 100.0f;
232 for(int i=0; i<args.length; i++) {
233 if(args[i].equals("-f")) {
234 i++;
235 freq = atof(args[i], freq);
236 }
237 }
238 final Synth01AL o = new Synth01AL();
239 o.init();
240 o.loop(freq);
241 o.exit();
242 }
243}
This class provides factory methods for generating AL and ALC objects.
Definition: ALFactory.java:62
static AL getAL()
Get the default AL object.
Definition: ALFactory.java:122
static ALC getALC()
Get the default ALC object.
Definition: ALFactory.java:136
A continuous simple on-thread immutable sine wave synthesizer.
Definition: Synth01AL.java:50
static final float PI
The value PI, i.e.
Definition: Synth01AL.java:52
static void waitForKey(final String message)
Definition: Synth01AL.java:61
static float atof(final String str, final float def)
Definition: Synth01AL.java:221
static final float TWO_PI
The value 2PI, i.e.
Definition: Synth01AL.java:55
static int findBestWaveCount(final float freq, final int sampleRate, final int minWaves, final int maxWaves)
Definition: Synth01AL.java:147
static void main(final String[] args)
Definition: Synth01AL.java:230
void alcDestroyContext(ALCcontext context)
Entry point (through function pointer) to C language function: void alcDestroyContext(ALCcontext * ...
boolean alcMakeContextCurrent(ALCcontext context)
Entry point (through function pointer) to C language function: ALCboolean alcMakeContextCurrent(ALC...
java.lang.String[] alcGetAllDeviceSpecifiers()
Fetches the names of the available ALC all capture device specifiers.
java.lang.String[] alcGetDeviceSpecifiers()
Fetches the names of the available ALC device specifiers.
boolean alcCloseDevice(ALCdevice device)
Entry point (through function pointer) to C language function: ALCboolean alcCloseDevice(ALCdevice ...
ALCdevice alcOpenDevice(String devicename)
Entry point (through function pointer) to C language function: ALCdevice * alcOpenDevice(const ALCc...
ALCcontext alcCreateContext(ALCdevice device, IntBuffer attrlist)
Entry point (through function pointer) to C language function: ALCcontext * alcCreateContext(ALCdev...
static final int AL_BUFFER
Define "AL_BUFFER" with expression '0x1009', CType: int.
static final int AL_PLAYING
Define "AL_PLAYING" with expression '0x1012', CType: int.
static final int AL_TRUE
Define "AL_TRUE" with expression '1', CType: int.
static final int AL_LOOPING
Define "AL_LOOPING" with expression '0x1007', CType: int.
static final int AL_NO_ERROR
Define "AL_NO_ERROR" with expression '0', CType: int.
static final int AL_FORMAT_MONO16
Define "AL_FORMAT_MONO16" with expression '0x1101', CType: int.
static final int AL_SOURCE_STATE
Define "AL_SOURCE_STATE" with expression '0x1010', CType: int.
void alBufferData(int buffer, int format, Buffer data, int size, int samplerate)
Entry point (through function pointer) to C language function: void alBufferData(ALuint buffer,...
int alGetError()
Entry point (through function pointer) to C language function: ALenum alGetError()
void alGenBuffers(int n, IntBuffer buffers)
Entry point (through function pointer) to C language function: void alGenBuffers(ALsizei n,...
void alDeleteSources(int n, IntBuffer sources)
Entry point (through function pointer) to C language function: void alDeleteSources(ALsizei n,...
void alSourcePlay(int source)
Entry point (through function pointer) to C language function: void alSourcePlay(ALuint source)
void alGetSourcei(int source, int param, IntBuffer value)
Entry point (through function pointer) to C language function: void alGetSourcei(ALuint source,...
void alSourcei(int source, int param, int value)
Entry point (through function pointer) to C language function: void alSourcei(ALuint source,...
void alSourceRewind(int source)
Entry point (through function pointer) to C language function: void alSourceRewind(ALuint source)
void alSourceStopv(int n, IntBuffer sources)
Entry point (through function pointer) to C language function: void alSourceStopv(ALsizei n,...
void alDeleteBuffers(int n, IntBuffer buffers)
Entry point (through function pointer) to C language function: void alDeleteBuffers(ALsizei n,...
void alGenSources(int n, IntBuffer sources)
Entry point (through function pointer) to C language function: void alGenSources(ALsizei n,...
String alGetString(int param)
Entry point (through function pointer) to C language function: const ALchar * alGetString(ALenum pa...