Remove deprecated, performance, and error event types
[openal-soft.git] / examples / alconvolve.c
blob4f0eb53d251e46950d2a102adde406d812892e3d
1 /*
2 * OpenAL Convolution Reverb Example
4 * Copyright (c) 2020 by Chris Robinson <chris.kcat@gmail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
25 /* This file contains an example for applying convolution reverb to a source. */
27 #include <assert.h>
28 #include <inttypes.h>
29 #include <limits.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
34 #include "sndfile.h"
36 #include "AL/al.h"
37 #include "AL/alext.h"
39 #include "common/alhelpers.h"
42 #ifndef AL_SOFT_convolution_reverb
43 #define AL_SOFT_convolution_reverb
44 #define AL_EFFECT_CONVOLUTION_REVERB_SOFT 0xA000
45 #endif
48 /* Effect object functions */
49 static LPALGENEFFECTS alGenEffects;
50 static LPALDELETEEFFECTS alDeleteEffects;
51 static LPALISEFFECT alIsEffect;
52 static LPALEFFECTI alEffecti;
53 static LPALEFFECTIV alEffectiv;
54 static LPALEFFECTF alEffectf;
55 static LPALEFFECTFV alEffectfv;
56 static LPALGETEFFECTI alGetEffecti;
57 static LPALGETEFFECTIV alGetEffectiv;
58 static LPALGETEFFECTF alGetEffectf;
59 static LPALGETEFFECTFV alGetEffectfv;
61 /* Auxiliary Effect Slot object functions */
62 static LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots;
63 static LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots;
64 static LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot;
65 static LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti;
66 static LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv;
67 static LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf;
68 static LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv;
69 static LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti;
70 static LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv;
71 static LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf;
72 static LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv;
75 /* This stuff defines a simple streaming player object, the same as alstream.c.
76 * Comments are removed for brevity, see alstream.c for more details.
78 #define NUM_BUFFERS 4
79 #define BUFFER_SAMPLES 8192
81 typedef struct StreamPlayer {
82 ALuint buffers[NUM_BUFFERS];
83 ALuint source;
85 SNDFILE *sndfile;
86 SF_INFO sfinfo;
87 float *membuf;
89 ALenum format;
90 } StreamPlayer;
92 static StreamPlayer *NewPlayer(void)
94 StreamPlayer *player;
96 player = calloc(1, sizeof(*player));
97 assert(player != NULL);
99 alGenBuffers(NUM_BUFFERS, player->buffers);
100 assert(alGetError() == AL_NO_ERROR && "Could not create buffers");
102 alGenSources(1, &player->source);
103 assert(alGetError() == AL_NO_ERROR && "Could not create source");
105 alSource3i(player->source, AL_POSITION, 0, 0, -1);
106 alSourcei(player->source, AL_SOURCE_RELATIVE, AL_TRUE);
107 alSourcei(player->source, AL_ROLLOFF_FACTOR, 0);
108 assert(alGetError() == AL_NO_ERROR && "Could not set source parameters");
110 return player;
113 static void ClosePlayerFile(StreamPlayer *player)
115 if(player->sndfile)
116 sf_close(player->sndfile);
117 player->sndfile = NULL;
119 free(player->membuf);
120 player->membuf = NULL;
123 static void DeletePlayer(StreamPlayer *player)
125 ClosePlayerFile(player);
127 alDeleteSources(1, &player->source);
128 alDeleteBuffers(NUM_BUFFERS, player->buffers);
129 if(alGetError() != AL_NO_ERROR)
130 fprintf(stderr, "Failed to delete object IDs\n");
132 memset(player, 0, sizeof(*player));
133 free(player);
136 static int OpenPlayerFile(StreamPlayer *player, const char *filename)
138 size_t frame_size;
140 ClosePlayerFile(player);
142 player->sndfile = sf_open(filename, SFM_READ, &player->sfinfo);
143 if(!player->sndfile)
145 fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(NULL));
146 return 0;
149 if(player->sfinfo.channels == 1)
150 player->format = AL_FORMAT_MONO_FLOAT32;
151 else if(player->sfinfo.channels == 2)
152 player->format = AL_FORMAT_STEREO_FLOAT32;
153 else if(player->sfinfo.channels == 6)
154 player->format = AL_FORMAT_51CHN32;
155 else
157 fprintf(stderr, "Unsupported channel count: %d\n", player->sfinfo.channels);
158 sf_close(player->sndfile);
159 player->sndfile = NULL;
160 return 0;
163 frame_size = (size_t)(BUFFER_SAMPLES * player->sfinfo.channels) * sizeof(float);
164 player->membuf = malloc(frame_size);
166 return 1;
169 static int StartPlayer(StreamPlayer *player)
171 ALsizei i;
173 alSourceRewind(player->source);
174 alSourcei(player->source, AL_BUFFER, 0);
176 for(i = 0;i < NUM_BUFFERS;i++)
178 sf_count_t slen = sf_readf_float(player->sndfile, player->membuf, BUFFER_SAMPLES);
179 if(slen < 1) break;
181 slen *= player->sfinfo.channels * (sf_count_t)sizeof(float);
182 alBufferData(player->buffers[i], player->format, player->membuf, (ALsizei)slen,
183 player->sfinfo.samplerate);
185 if(alGetError() != AL_NO_ERROR)
187 fprintf(stderr, "Error buffering for playback\n");
188 return 0;
191 alSourceQueueBuffers(player->source, i, player->buffers);
192 alSourcePlay(player->source);
193 if(alGetError() != AL_NO_ERROR)
195 fprintf(stderr, "Error starting playback\n");
196 return 0;
199 return 1;
202 static int UpdatePlayer(StreamPlayer *player)
204 ALint processed, state;
206 alGetSourcei(player->source, AL_SOURCE_STATE, &state);
207 alGetSourcei(player->source, AL_BUFFERS_PROCESSED, &processed);
208 if(alGetError() != AL_NO_ERROR)
210 fprintf(stderr, "Error checking source state\n");
211 return 0;
214 while(processed > 0)
216 ALuint bufid;
217 sf_count_t slen;
219 alSourceUnqueueBuffers(player->source, 1, &bufid);
220 processed--;
222 slen = sf_readf_float(player->sndfile, player->membuf, BUFFER_SAMPLES);
223 if(slen > 0)
225 slen *= player->sfinfo.channels * (sf_count_t)sizeof(float);
226 alBufferData(bufid, player->format, player->membuf, (ALsizei)slen,
227 player->sfinfo.samplerate);
228 alSourceQueueBuffers(player->source, 1, &bufid);
230 if(alGetError() != AL_NO_ERROR)
232 fprintf(stderr, "Error buffering data\n");
233 return 0;
237 if(state != AL_PLAYING && state != AL_PAUSED)
239 ALint queued;
241 alGetSourcei(player->source, AL_BUFFERS_QUEUED, &queued);
242 if(queued == 0)
243 return 0;
245 alSourcePlay(player->source);
246 if(alGetError() != AL_NO_ERROR)
248 fprintf(stderr, "Error restarting playback\n");
249 return 0;
253 return 1;
257 /* CreateEffect creates a new OpenAL effect object with a convolution reverb
258 * type, and returns the new effect ID.
260 static ALuint CreateEffect(void)
262 ALuint effect = 0;
263 ALenum err;
265 printf("Using Convolution Reverb\n");
267 /* Create the effect object and set the convolution reverb effect type. */
268 alGenEffects(1, &effect);
269 alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_CONVOLUTION_REVERB_SOFT);
271 /* Check if an error occured, and clean up if so. */
272 err = alGetError();
273 if(err != AL_NO_ERROR)
275 fprintf(stderr, "OpenAL error: %s\n", alGetString(err));
276 if(alIsEffect(effect))
277 alDeleteEffects(1, &effect);
278 return 0;
281 return effect;
284 /* LoadBuffer loads the named audio file into an OpenAL buffer object, and
285 * returns the new buffer ID.
287 static ALuint LoadSound(const char *filename)
289 const char *namepart;
290 ALenum err, format;
291 ALuint buffer;
292 SNDFILE *sndfile;
293 SF_INFO sfinfo;
294 float *membuf;
295 sf_count_t num_frames;
296 ALsizei num_bytes;
298 /* Open the audio file and check that it's usable. */
299 sndfile = sf_open(filename, SFM_READ, &sfinfo);
300 if(!sndfile)
302 fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile));
303 return 0;
305 if(sfinfo.frames < 1 || sfinfo.frames > (sf_count_t)(INT_MAX/sizeof(float))/sfinfo.channels)
307 fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames);
308 sf_close(sndfile);
309 return 0;
312 /* Get the sound format, and figure out the OpenAL format. Use floats since
313 * impulse responses will usually have more than 16-bit precision.
315 if(sfinfo.channels == 1)
316 format = AL_FORMAT_MONO_FLOAT32;
317 else if(sfinfo.channels == 2)
318 format = AL_FORMAT_STEREO_FLOAT32;
319 else
321 fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels);
322 sf_close(sndfile);
323 return 0;
326 namepart = strrchr(filename, '/');
327 if(namepart || (namepart=strrchr(filename, '\\')))
328 namepart++;
329 else
330 namepart = filename;
331 printf("Loading: %s (%s, %dhz, %" PRId64 " samples / %.2f seconds)\n", namepart,
332 FormatName(format), sfinfo.samplerate, sfinfo.frames,
333 (double)sfinfo.frames / sfinfo.samplerate);
334 fflush(stdout);
336 /* Decode the whole audio file to a buffer. */
337 membuf = malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(float));
339 num_frames = sf_readf_float(sndfile, membuf, sfinfo.frames);
340 if(num_frames < 1)
342 free(membuf);
343 sf_close(sndfile);
344 fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames);
345 return 0;
347 num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(float);
349 /* Buffer the audio data into a new buffer object, then free the data and
350 * close the file.
352 buffer = 0;
353 alGenBuffers(1, &buffer);
354 alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate);
356 free(membuf);
357 sf_close(sndfile);
359 /* Check if an error occured, and clean up if so. */
360 err = alGetError();
361 if(err != AL_NO_ERROR)
363 fprintf(stderr, "OpenAL Error: %s\n", alGetString(err));
364 if(buffer && alIsBuffer(buffer))
365 alDeleteBuffers(1, &buffer);
366 return 0;
369 return buffer;
373 int main(int argc, char **argv)
375 ALuint ir_buffer, effect, slot;
376 StreamPlayer *player;
377 int i;
379 /* Print out usage if no arguments were specified */
380 if(argc < 2)
382 fprintf(stderr, "Usage: %s [-device <name>] <impulse response file> <filenames...>\n",
383 argv[0]);
384 return 1;
387 argv++; argc--;
388 if(InitAL(&argv, &argc) != 0)
389 return 1;
391 if(!alIsExtensionPresent("AL_SOFTX_convolution_reverb"))
393 CloseAL();
394 fprintf(stderr, "Error: Convolution revern not supported\n");
395 return 1;
398 if(argc < 2)
400 CloseAL();
401 fprintf(stderr, "Error: Missing impulse response or sound files\n");
402 return 1;
405 /* Define a macro to help load the function pointers. */
406 #define LOAD_PROC(T, x) ((x) = (T)alGetProcAddress(#x))
407 LOAD_PROC(LPALGENEFFECTS, alGenEffects);
408 LOAD_PROC(LPALDELETEEFFECTS, alDeleteEffects);
409 LOAD_PROC(LPALISEFFECT, alIsEffect);
410 LOAD_PROC(LPALEFFECTI, alEffecti);
411 LOAD_PROC(LPALEFFECTIV, alEffectiv);
412 LOAD_PROC(LPALEFFECTF, alEffectf);
413 LOAD_PROC(LPALEFFECTFV, alEffectfv);
414 LOAD_PROC(LPALGETEFFECTI, alGetEffecti);
415 LOAD_PROC(LPALGETEFFECTIV, alGetEffectiv);
416 LOAD_PROC(LPALGETEFFECTF, alGetEffectf);
417 LOAD_PROC(LPALGETEFFECTFV, alGetEffectfv);
419 LOAD_PROC(LPALGENAUXILIARYEFFECTSLOTS, alGenAuxiliaryEffectSlots);
420 LOAD_PROC(LPALDELETEAUXILIARYEFFECTSLOTS, alDeleteAuxiliaryEffectSlots);
421 LOAD_PROC(LPALISAUXILIARYEFFECTSLOT, alIsAuxiliaryEffectSlot);
422 LOAD_PROC(LPALAUXILIARYEFFECTSLOTI, alAuxiliaryEffectSloti);
423 LOAD_PROC(LPALAUXILIARYEFFECTSLOTIV, alAuxiliaryEffectSlotiv);
424 LOAD_PROC(LPALAUXILIARYEFFECTSLOTF, alAuxiliaryEffectSlotf);
425 LOAD_PROC(LPALAUXILIARYEFFECTSLOTFV, alAuxiliaryEffectSlotfv);
426 LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTI, alGetAuxiliaryEffectSloti);
427 LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTIV, alGetAuxiliaryEffectSlotiv);
428 LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf);
429 LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv);
430 #undef LOAD_PROC
432 /* Load the reverb into an effect. */
433 effect = CreateEffect();
434 if(!effect)
436 CloseAL();
437 return 1;
440 /* Load the impulse response sound into a buffer. */
441 ir_buffer = LoadSound(argv[0]);
442 if(!ir_buffer)
444 alDeleteEffects(1, &effect);
445 CloseAL();
446 return 1;
449 /* Create the effect slot object. This is what "plays" an effect on sources
450 * that connect to it.
452 slot = 0;
453 alGenAuxiliaryEffectSlots(1, &slot);
455 /* Set the impulse response sound buffer on the effect slot. This allows
456 * effects to access it as needed. In this case, convolution reverb uses it
457 * as the filter source. NOTE: Unlike the effect object, the buffer *is*
458 * kept referenced and may not be changed or deleted as long as it's set,
459 * just like with a source. When another buffer is set, or the effect slot
460 * is deleted, the buffer reference is released.
462 * The effect slot's gain is reduced because the impulse responses I've
463 * tested with result in excessively loud reverb. Is that normal? Even with
464 * this, it seems a bit on the loud side.
466 * Also note: unlike standard or EAX reverb, there is no automatic
467 * attenuation of a source's reverb response with distance, so the reverb
468 * will remain full volume regardless of a given sound's distance from the
469 * listener. You can use a send filter to alter a given source's
470 * contribution to reverb.
472 alAuxiliaryEffectSloti(slot, AL_BUFFER, (ALint)ir_buffer);
473 alAuxiliaryEffectSlotf(slot, AL_EFFECTSLOT_GAIN, 1.0f / 16.0f);
474 alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, (ALint)effect);
475 assert(alGetError()==AL_NO_ERROR && "Failed to set effect slot");
477 player = NewPlayer();
478 /* Connect the player's source to the effect slot. */
479 alSource3i(player->source, AL_AUXILIARY_SEND_FILTER, (ALint)slot, 0, AL_FILTER_NULL);
480 assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
482 /* Play each file listed on the command line */
483 for(i = 1;i < argc;i++)
485 const char *namepart;
487 if(!OpenPlayerFile(player, argv[i]))
488 continue;
490 namepart = strrchr(argv[i], '/');
491 if(namepart || (namepart=strrchr(argv[i], '\\')))
492 namepart++;
493 else
494 namepart = argv[i];
496 printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->format),
497 player->sfinfo.samplerate);
498 fflush(stdout);
500 if(!StartPlayer(player))
502 ClosePlayerFile(player);
503 continue;
506 while(UpdatePlayer(player))
507 al_nssleep(10000000);
509 ClosePlayerFile(player);
511 printf("Done.\n");
513 /* All files done. Delete the player and effect resources, and close down
514 * OpenAL.
516 DeletePlayer(player);
517 player = NULL;
519 alDeleteAuxiliaryEffectSlots(1, &slot);
520 alDeleteEffects(1, &effect);
521 alDeleteBuffers(1, &ir_buffer);
523 CloseAL();
525 return 0;