Add an in-progress extension string for convolution reverb
[openal-soft.git] / examples / alconvolve.c
blob05804dbe36dca8169ff863b5feb30dcade4aabff
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 ALenum err, format;
290 ALuint buffer;
291 SNDFILE *sndfile;
292 SF_INFO sfinfo;
293 float *membuf;
294 sf_count_t num_frames;
295 ALsizei num_bytes;
297 /* Open the audio file and check that it's usable. */
298 sndfile = sf_open(filename, SFM_READ, &sfinfo);
299 if(!sndfile)
301 fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile));
302 return 0;
304 if(sfinfo.frames < 1 || sfinfo.frames > (sf_count_t)(INT_MAX/sizeof(float))/sfinfo.channels)
306 fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames);
307 sf_close(sndfile);
308 return 0;
311 /* Get the sound format, and figure out the OpenAL format. Use floats since
312 * impulse responses will usually have more than 16-bit precision.
314 if(sfinfo.channels == 1)
315 format = AL_FORMAT_MONO_FLOAT32;
316 else if(sfinfo.channels == 2)
317 format = AL_FORMAT_STEREO_FLOAT32;
318 else
320 fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels);
321 sf_close(sndfile);
322 return 0;
325 /* Decode the whole audio file to a buffer. */
326 membuf = malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(float));
328 num_frames = sf_readf_float(sndfile, membuf, sfinfo.frames);
329 if(num_frames < 1)
331 free(membuf);
332 sf_close(sndfile);
333 fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames);
334 return 0;
336 num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(float);
338 /* Buffer the audio data into a new buffer object, then free the data and
339 * close the file.
341 buffer = 0;
342 alGenBuffers(1, &buffer);
343 alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate);
345 free(membuf);
346 sf_close(sndfile);
348 /* Check if an error occured, and clean up if so. */
349 err = alGetError();
350 if(err != AL_NO_ERROR)
352 fprintf(stderr, "OpenAL Error: %s\n", alGetString(err));
353 if(buffer && alIsBuffer(buffer))
354 alDeleteBuffers(1, &buffer);
355 return 0;
358 return buffer;
362 int main(int argc, char **argv)
364 ALuint ir_buffer, effect, slot;
365 StreamPlayer *player;
366 int i;
368 /* Print out usage if no arguments were specified */
369 if(argc < 2)
371 fprintf(stderr, "Usage: %s [-device <name>] <impulse response file> <filenames...>\n",
372 argv[0]);
373 return 1;
376 argv++; argc--;
377 if(InitAL(&argv, &argc) != 0)
378 return 1;
380 if(!alIsExtensionPresent("AL_SOFTX_convolution_reverb"))
382 CloseAL();
383 fprintf(stderr, "Error: Convolution revern not supported\n");
384 return 1;
387 if(argc < 2)
389 CloseAL();
390 fprintf(stderr, "Error: Missing impulse response or sound files\n");
391 return 1;
394 /* Define a macro to help load the function pointers. */
395 #define LOAD_PROC(T, x) ((x) = (T)alGetProcAddress(#x))
396 LOAD_PROC(LPALGENEFFECTS, alGenEffects);
397 LOAD_PROC(LPALDELETEEFFECTS, alDeleteEffects);
398 LOAD_PROC(LPALISEFFECT, alIsEffect);
399 LOAD_PROC(LPALEFFECTI, alEffecti);
400 LOAD_PROC(LPALEFFECTIV, alEffectiv);
401 LOAD_PROC(LPALEFFECTF, alEffectf);
402 LOAD_PROC(LPALEFFECTFV, alEffectfv);
403 LOAD_PROC(LPALGETEFFECTI, alGetEffecti);
404 LOAD_PROC(LPALGETEFFECTIV, alGetEffectiv);
405 LOAD_PROC(LPALGETEFFECTF, alGetEffectf);
406 LOAD_PROC(LPALGETEFFECTFV, alGetEffectfv);
408 LOAD_PROC(LPALGENAUXILIARYEFFECTSLOTS, alGenAuxiliaryEffectSlots);
409 LOAD_PROC(LPALDELETEAUXILIARYEFFECTSLOTS, alDeleteAuxiliaryEffectSlots);
410 LOAD_PROC(LPALISAUXILIARYEFFECTSLOT, alIsAuxiliaryEffectSlot);
411 LOAD_PROC(LPALAUXILIARYEFFECTSLOTI, alAuxiliaryEffectSloti);
412 LOAD_PROC(LPALAUXILIARYEFFECTSLOTIV, alAuxiliaryEffectSlotiv);
413 LOAD_PROC(LPALAUXILIARYEFFECTSLOTF, alAuxiliaryEffectSlotf);
414 LOAD_PROC(LPALAUXILIARYEFFECTSLOTFV, alAuxiliaryEffectSlotfv);
415 LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTI, alGetAuxiliaryEffectSloti);
416 LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTIV, alGetAuxiliaryEffectSlotiv);
417 LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf);
418 LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv);
419 #undef LOAD_PROC
421 /* Load the impulse response sound into a buffer. */
422 ir_buffer = LoadSound(argv[0]);
423 if(!ir_buffer)
425 CloseAL();
426 return 1;
429 /* Load the reverb into an effect. */
430 effect = CreateEffect();
431 if(!effect)
433 alDeleteBuffers(1, &ir_buffer);
434 CloseAL();
435 return 1;
438 /* Create the effect slot object. This is what "plays" an effect on sources
439 * that connect to it.
441 slot = 0;
442 alGenAuxiliaryEffectSlots(1, &slot);
444 /* Set the impulse response sound buffer on the effect slot. This allows
445 * effects to access it as needed. In this case, convolution reverb uses it
446 * as the filter source. NOTE: Unlike the effect object, the buffer *is*
447 * kept referenced and may not be changed or deleted as long as it's set,
448 * just like with a source. When another buffer is set, or the effect slot
449 * is deleted, the buffer reference is released.
451 * The effect slot's gain is reduced because the impulse responses I've
452 * tested with result in excessively loud reverb. Is that normal? Even with
453 * this, it seems a bit on the loud side.
455 * Also note: unlike standard or EAX reverb, there is no automatic
456 * attenuation of a source's reverb response with distance, so the reverb
457 * will remain full volume regardless of a given sound's distance from the
458 * listener. You can use a send filter to alter a given source's
459 * contribution to reverb.
461 alAuxiliaryEffectSloti(slot, AL_BUFFER, (ALint)ir_buffer);
462 alAuxiliaryEffectSlotf(slot, AL_EFFECTSLOT_GAIN, 1.0f / 16.0f);
463 alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, (ALint)effect);
464 assert(alGetError()==AL_NO_ERROR && "Failed to set effect slot");
466 player = NewPlayer();
467 /* Connect the player's source to the effect slot. */
468 alSource3i(player->source, AL_AUXILIARY_SEND_FILTER, (ALint)slot, 0, AL_FILTER_NULL);
469 assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
471 /* Play each file listed on the command line */
472 for(i = 1;i < argc;i++)
474 const char *namepart;
476 if(!OpenPlayerFile(player, argv[i]))
477 continue;
479 namepart = strrchr(argv[i], '/');
480 if(namepart || (namepart=strrchr(argv[i], '\\')))
481 namepart++;
482 else
483 namepart = argv[i];
485 printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->format),
486 player->sfinfo.samplerate);
487 fflush(stdout);
489 if(!StartPlayer(player))
491 ClosePlayerFile(player);
492 continue;
495 while(UpdatePlayer(player))
496 al_nssleep(10000000);
498 ClosePlayerFile(player);
500 printf("Done.\n");
502 /* All files done. Delete the player and effect resources, and close down
503 * OpenAL.
505 DeletePlayer(player);
506 player = NULL;
508 alDeleteAuxiliaryEffectSlots(1, &slot);
509 alDeleteEffects(1, &effect);
510 alDeleteBuffers(1, &ir_buffer);
512 CloseAL();
514 return 0;