almultireverb.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. /*
  2. * OpenAL Multi-Zone Reverb Example
  3. *
  4. * Copyright (c) 2018 by Chris Robinson <[email protected]>
  5. *
  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:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  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.
  23. */
  24. /* This file contains an example for controlling multiple reverb zones to
  25. * smoothly transition between reverb environments. The general concept is to
  26. * extend single-reverb by also tracking the closest adjacent environment, and
  27. * utilize EAX Reverb's panning vectors to position them relative to the
  28. * listener.
  29. */
  30. #include <assert.h>
  31. #include <inttypes.h>
  32. #include <limits.h>
  33. #include <math.h>
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include "sndfile.h"
  38. #include "AL/al.h"
  39. #include "AL/alc.h"
  40. #include "AL/efx.h"
  41. #include "AL/efx-presets.h"
  42. #include "common/alhelpers.h"
  43. #include "win_main_utf8.h"
  44. #ifndef M_PI
  45. #define M_PI 3.14159265358979323846
  46. #endif
  47. /* Filter object functions */
  48. static LPALGENFILTERS alGenFilters;
  49. static LPALDELETEFILTERS alDeleteFilters;
  50. static LPALISFILTER alIsFilter;
  51. static LPALFILTERI alFilteri;
  52. static LPALFILTERIV alFilteriv;
  53. static LPALFILTERF alFilterf;
  54. static LPALFILTERFV alFilterfv;
  55. static LPALGETFILTERI alGetFilteri;
  56. static LPALGETFILTERIV alGetFilteriv;
  57. static LPALGETFILTERF alGetFilterf;
  58. static LPALGETFILTERFV alGetFilterfv;
  59. /* Effect object functions */
  60. static LPALGENEFFECTS alGenEffects;
  61. static LPALDELETEEFFECTS alDeleteEffects;
  62. static LPALISEFFECT alIsEffect;
  63. static LPALEFFECTI alEffecti;
  64. static LPALEFFECTIV alEffectiv;
  65. static LPALEFFECTF alEffectf;
  66. static LPALEFFECTFV alEffectfv;
  67. static LPALGETEFFECTI alGetEffecti;
  68. static LPALGETEFFECTIV alGetEffectiv;
  69. static LPALGETEFFECTF alGetEffectf;
  70. static LPALGETEFFECTFV alGetEffectfv;
  71. /* Auxiliary Effect Slot object functions */
  72. static LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots;
  73. static LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots;
  74. static LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot;
  75. static LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti;
  76. static LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv;
  77. static LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf;
  78. static LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv;
  79. static LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti;
  80. static LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv;
  81. static LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf;
  82. static LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv;
  83. /* LoadEffect loads the given initial reverb properties into the given OpenAL
  84. * effect object, and returns non-zero on success.
  85. */
  86. static int LoadEffect(ALuint effect, const EFXEAXREVERBPROPERTIES *reverb)
  87. {
  88. ALenum err;
  89. alGetError();
  90. /* Prepare the effect for EAX Reverb (standard reverb doesn't contain
  91. * the needed panning vectors).
  92. */
  93. alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);
  94. err = alGetError();
  95. if(err != AL_NO_ERROR)
  96. {
  97. fprintf(stderr, "Failed to set EAX Reverb: %s (0x%04x)\n", alGetString(err), err);
  98. return 0;
  99. }
  100. /* Load the reverb properties. */
  101. alEffectf(effect, AL_EAXREVERB_DENSITY, reverb->flDensity);
  102. alEffectf(effect, AL_EAXREVERB_DIFFUSION, reverb->flDiffusion);
  103. alEffectf(effect, AL_EAXREVERB_GAIN, reverb->flGain);
  104. alEffectf(effect, AL_EAXREVERB_GAINHF, reverb->flGainHF);
  105. alEffectf(effect, AL_EAXREVERB_GAINLF, reverb->flGainLF);
  106. alEffectf(effect, AL_EAXREVERB_DECAY_TIME, reverb->flDecayTime);
  107. alEffectf(effect, AL_EAXREVERB_DECAY_HFRATIO, reverb->flDecayHFRatio);
  108. alEffectf(effect, AL_EAXREVERB_DECAY_LFRATIO, reverb->flDecayLFRatio);
  109. alEffectf(effect, AL_EAXREVERB_REFLECTIONS_GAIN, reverb->flReflectionsGain);
  110. alEffectf(effect, AL_EAXREVERB_REFLECTIONS_DELAY, reverb->flReflectionsDelay);
  111. alEffectfv(effect, AL_EAXREVERB_REFLECTIONS_PAN, reverb->flReflectionsPan);
  112. alEffectf(effect, AL_EAXREVERB_LATE_REVERB_GAIN, reverb->flLateReverbGain);
  113. alEffectf(effect, AL_EAXREVERB_LATE_REVERB_DELAY, reverb->flLateReverbDelay);
  114. alEffectfv(effect, AL_EAXREVERB_LATE_REVERB_PAN, reverb->flLateReverbPan);
  115. alEffectf(effect, AL_EAXREVERB_ECHO_TIME, reverb->flEchoTime);
  116. alEffectf(effect, AL_EAXREVERB_ECHO_DEPTH, reverb->flEchoDepth);
  117. alEffectf(effect, AL_EAXREVERB_MODULATION_TIME, reverb->flModulationTime);
  118. alEffectf(effect, AL_EAXREVERB_MODULATION_DEPTH, reverb->flModulationDepth);
  119. alEffectf(effect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, reverb->flAirAbsorptionGainHF);
  120. alEffectf(effect, AL_EAXREVERB_HFREFERENCE, reverb->flHFReference);
  121. alEffectf(effect, AL_EAXREVERB_LFREFERENCE, reverb->flLFReference);
  122. alEffectf(effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, reverb->flRoomRolloffFactor);
  123. alEffecti(effect, AL_EAXREVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit);
  124. /* Check if an error occurred, and return failure if so. */
  125. err = alGetError();
  126. if(err != AL_NO_ERROR)
  127. {
  128. fprintf(stderr, "Error setting up reverb: %s\n", alGetString(err));
  129. return 0;
  130. }
  131. return 1;
  132. }
  133. /* LoadBuffer loads the named audio file into an OpenAL buffer object, and
  134. * returns the new buffer ID.
  135. */
  136. static ALuint LoadSound(const char *filename)
  137. {
  138. ALenum err, format;
  139. ALuint buffer;
  140. SNDFILE *sndfile;
  141. SF_INFO sfinfo;
  142. short *membuf;
  143. sf_count_t num_frames;
  144. ALsizei num_bytes;
  145. /* Open the audio file and check that it's usable. */
  146. sndfile = sf_open(filename, SFM_READ, &sfinfo);
  147. if(!sndfile)
  148. {
  149. fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile));
  150. return 0;
  151. }
  152. if(sfinfo.frames < 1 || sfinfo.frames > (sf_count_t)(INT_MAX/sizeof(short))/sfinfo.channels)
  153. {
  154. fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames);
  155. sf_close(sndfile);
  156. return 0;
  157. }
  158. /* Get the sound format, and figure out the OpenAL format */
  159. if(sfinfo.channels == 1)
  160. format = AL_FORMAT_MONO16;
  161. else if(sfinfo.channels == 2)
  162. format = AL_FORMAT_STEREO16;
  163. else
  164. {
  165. fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels);
  166. sf_close(sndfile);
  167. return 0;
  168. }
  169. /* Decode the whole audio file to a buffer. */
  170. membuf = malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(short));
  171. num_frames = sf_readf_short(sndfile, membuf, sfinfo.frames);
  172. if(num_frames < 1)
  173. {
  174. free(membuf);
  175. sf_close(sndfile);
  176. fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames);
  177. return 0;
  178. }
  179. num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(short);
  180. /* Buffer the audio data into a new buffer object, then free the data and
  181. * close the file.
  182. */
  183. buffer = 0;
  184. alGenBuffers(1, &buffer);
  185. alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate);
  186. free(membuf);
  187. sf_close(sndfile);
  188. /* Check if an error occurred, and clean up if so. */
  189. err = alGetError();
  190. if(err != AL_NO_ERROR)
  191. {
  192. fprintf(stderr, "OpenAL Error: %s\n", alGetString(err));
  193. if(buffer && alIsBuffer(buffer))
  194. alDeleteBuffers(1, &buffer);
  195. return 0;
  196. }
  197. return buffer;
  198. }
  199. /* Helper to calculate the dot-product of the two given vectors. */
  200. static ALfloat dot_product(const ALfloat vec0[3], const ALfloat vec1[3])
  201. {
  202. return vec0[0]*vec1[0] + vec0[1]*vec1[1] + vec0[2]*vec1[2];
  203. }
  204. /* Helper to normalize a given vector. */
  205. static void normalize(ALfloat vec[3])
  206. {
  207. ALfloat mag = sqrtf(dot_product(vec, vec));
  208. if(mag > 0.00001f)
  209. {
  210. vec[0] /= mag;
  211. vec[1] /= mag;
  212. vec[2] /= mag;
  213. }
  214. else
  215. {
  216. vec[0] = 0.0f;
  217. vec[1] = 0.0f;
  218. vec[2] = 0.0f;
  219. }
  220. }
  221. /* The main update function to update the listener and environment effects. */
  222. static void UpdateListenerAndEffects(float timediff, const ALuint slots[2], const ALuint effects[2], const EFXEAXREVERBPROPERTIES reverbs[2])
  223. {
  224. static const ALfloat listener_move_scale = 10.0f;
  225. /* Individual reverb zones are connected via "portals". Each portal has a
  226. * position (center point of the connecting area), a normal (facing
  227. * direction), and a radius (approximate size of the connecting area).
  228. */
  229. const ALfloat portal_pos[3] = { 0.0f, 0.0f, 0.0f };
  230. const ALfloat portal_norm[3] = { sqrtf(0.5f), 0.0f, -sqrtf(0.5f) };
  231. const ALfloat portal_radius = 2.5f;
  232. ALfloat other_dir[3], this_dir[3];
  233. ALfloat listener_pos[3];
  234. ALfloat local_norm[3];
  235. ALfloat local_dir[3];
  236. ALfloat near_edge[3];
  237. ALfloat far_edge[3];
  238. ALfloat dist, edist;
  239. /* Update the listener position for the amount of time passed. This uses a
  240. * simple triangular LFO to offset the position (moves along the X axis
  241. * between -listener_move_scale and +listener_move_scale for each
  242. * transition).
  243. */
  244. listener_pos[0] = (fabsf(2.0f - timediff/2.0f) - 1.0f) * listener_move_scale;
  245. listener_pos[1] = 0.0f;
  246. listener_pos[2] = 0.0f;
  247. alListenerfv(AL_POSITION, listener_pos);
  248. /* Calculate local_dir, which represents the listener-relative point to the
  249. * adjacent zone (should also include orientation). Because EAX Reverb uses
  250. * left-handed coordinates instead of right-handed like the rest of OpenAL,
  251. * negate Z for the local values.
  252. */
  253. local_dir[0] = portal_pos[0] - listener_pos[0];
  254. local_dir[1] = portal_pos[1] - listener_pos[1];
  255. local_dir[2] = -(portal_pos[2] - listener_pos[2]);
  256. /* A normal application would also rotate the portal's normal given the
  257. * listener orientation, to get the listener-relative normal.
  258. */
  259. local_norm[0] = portal_norm[0];
  260. local_norm[1] = portal_norm[1];
  261. local_norm[2] = -portal_norm[2];
  262. /* Calculate the distance from the listener to the portal, and ensure it's
  263. * far enough away to not suffer severe floating-point precision issues.
  264. */
  265. dist = sqrtf(dot_product(local_dir, local_dir));
  266. if(dist > 0.00001f)
  267. {
  268. const EFXEAXREVERBPROPERTIES *other_reverb, *this_reverb;
  269. ALuint other_effect, this_effect;
  270. ALfloat magnitude, dir_dot_norm;
  271. /* Normalize the direction to the portal. */
  272. local_dir[0] /= dist;
  273. local_dir[1] /= dist;
  274. local_dir[2] /= dist;
  275. /* Calculate the dot product of the portal's local direction and local
  276. * normal, which is used for angular and side checks later on.
  277. */
  278. dir_dot_norm = dot_product(local_dir, local_norm);
  279. /* Figure out which zone we're in. */
  280. if(dir_dot_norm <= 0.0f)
  281. {
  282. /* We're in front of the portal, so we're in Zone 0. */
  283. this_effect = effects[0];
  284. other_effect = effects[1];
  285. this_reverb = &reverbs[0];
  286. other_reverb = &reverbs[1];
  287. }
  288. else
  289. {
  290. /* We're behind the portal, so we're in Zone 1. */
  291. this_effect = effects[1];
  292. other_effect = effects[0];
  293. this_reverb = &reverbs[1];
  294. other_reverb = &reverbs[0];
  295. }
  296. /* Calculate the listener-relative extents of the portal. */
  297. /* First, project the listener-to-portal vector onto the portal's plane
  298. * to get the portal-relative direction along the plane that goes away
  299. * from the listener (toward the farthest edge of the portal).
  300. */
  301. far_edge[0] = local_dir[0] - local_norm[0]*dir_dot_norm;
  302. far_edge[1] = local_dir[1] - local_norm[1]*dir_dot_norm;
  303. far_edge[2] = local_dir[2] - local_norm[2]*dir_dot_norm;
  304. edist = sqrtf(dot_product(far_edge, far_edge));
  305. if(edist > 0.0001f)
  306. {
  307. /* Rescale the portal-relative vector to be at the radius edge. */
  308. ALfloat mag = portal_radius / edist;
  309. far_edge[0] *= mag;
  310. far_edge[1] *= mag;
  311. far_edge[2] *= mag;
  312. /* Calculate the closest edge of the portal by negating the
  313. * farthest, and add an offset to make them both relative to the
  314. * listener.
  315. */
  316. near_edge[0] = local_dir[0]*dist - far_edge[0];
  317. near_edge[1] = local_dir[1]*dist - far_edge[1];
  318. near_edge[2] = local_dir[2]*dist - far_edge[2];
  319. far_edge[0] += local_dir[0]*dist;
  320. far_edge[1] += local_dir[1]*dist;
  321. far_edge[2] += local_dir[2]*dist;
  322. /* Normalize the listener-relative extents of the portal, then
  323. * calculate the panning magnitude for the other zone given the
  324. * apparent size of the opening. The panning magnitude affects the
  325. * envelopment of the environment, with 1 being a point, 0.5 being
  326. * half coverage around the listener, and 0 being full coverage.
  327. */
  328. normalize(far_edge);
  329. normalize(near_edge);
  330. magnitude = 1.0f - acosf(dot_product(far_edge, near_edge))/(float)(M_PI*2.0);
  331. /* Recalculate the panning direction, to be directly between the
  332. * direction of the two extents.
  333. */
  334. local_dir[0] = far_edge[0] + near_edge[0];
  335. local_dir[1] = far_edge[1] + near_edge[1];
  336. local_dir[2] = far_edge[2] + near_edge[2];
  337. normalize(local_dir);
  338. }
  339. else
  340. {
  341. /* If we get here, the listener is directly in front of or behind
  342. * the center of the portal, making all aperture edges effectively
  343. * equidistant. Calculating the panning magnitude is simplified,
  344. * using the arctangent of the radius and distance.
  345. */
  346. magnitude = 1.0f - (atan2f(portal_radius, dist) / (float)M_PI);
  347. }
  348. /* Scale the other zone's panning vector. */
  349. other_dir[0] = local_dir[0] * magnitude;
  350. other_dir[1] = local_dir[1] * magnitude;
  351. other_dir[2] = local_dir[2] * magnitude;
  352. /* Pan the current zone to the opposite direction of the portal, and
  353. * take the remaining percentage of the portal's magnitude.
  354. */
  355. this_dir[0] = local_dir[0] * (magnitude-1.0f);
  356. this_dir[1] = local_dir[1] * (magnitude-1.0f);
  357. this_dir[2] = local_dir[2] * (magnitude-1.0f);
  358. /* Now set the effects' panning vectors and gain. Energy is shared
  359. * between environments, so attenuate according to each zone's
  360. * contribution (note: gain^2 = energy).
  361. */
  362. alEffectf(this_effect, AL_EAXREVERB_REFLECTIONS_GAIN, this_reverb->flReflectionsGain * sqrtf(magnitude));
  363. alEffectf(this_effect, AL_EAXREVERB_LATE_REVERB_GAIN, this_reverb->flLateReverbGain * sqrtf(magnitude));
  364. alEffectfv(this_effect, AL_EAXREVERB_REFLECTIONS_PAN, this_dir);
  365. alEffectfv(this_effect, AL_EAXREVERB_LATE_REVERB_PAN, this_dir);
  366. alEffectf(other_effect, AL_EAXREVERB_REFLECTIONS_GAIN, other_reverb->flReflectionsGain * sqrtf(1.0f-magnitude));
  367. alEffectf(other_effect, AL_EAXREVERB_LATE_REVERB_GAIN, other_reverb->flLateReverbGain * sqrtf(1.0f-magnitude));
  368. alEffectfv(other_effect, AL_EAXREVERB_REFLECTIONS_PAN, other_dir);
  369. alEffectfv(other_effect, AL_EAXREVERB_LATE_REVERB_PAN, other_dir);
  370. }
  371. else
  372. {
  373. /* We're practically in the center of the portal. Give the panning
  374. * vectors a 50/50 split, with Zone 0 covering the half in front of
  375. * the normal, and Zone 1 covering the half behind.
  376. */
  377. this_dir[0] = local_norm[0] / 2.0f;
  378. this_dir[1] = local_norm[1] / 2.0f;
  379. this_dir[2] = local_norm[2] / 2.0f;
  380. other_dir[0] = local_norm[0] / -2.0f;
  381. other_dir[1] = local_norm[1] / -2.0f;
  382. other_dir[2] = local_norm[2] / -2.0f;
  383. alEffectf(effects[0], AL_EAXREVERB_REFLECTIONS_GAIN, reverbs[0].flReflectionsGain * sqrtf(0.5f));
  384. alEffectf(effects[0], AL_EAXREVERB_LATE_REVERB_GAIN, reverbs[0].flLateReverbGain * sqrtf(0.5f));
  385. alEffectfv(effects[0], AL_EAXREVERB_REFLECTIONS_PAN, this_dir);
  386. alEffectfv(effects[0], AL_EAXREVERB_LATE_REVERB_PAN, this_dir);
  387. alEffectf(effects[1], AL_EAXREVERB_REFLECTIONS_GAIN, reverbs[1].flReflectionsGain * sqrtf(0.5f));
  388. alEffectf(effects[1], AL_EAXREVERB_LATE_REVERB_GAIN, reverbs[1].flLateReverbGain * sqrtf(0.5f));
  389. alEffectfv(effects[1], AL_EAXREVERB_REFLECTIONS_PAN, other_dir);
  390. alEffectfv(effects[1], AL_EAXREVERB_LATE_REVERB_PAN, other_dir);
  391. }
  392. /* Finally, update the effect slots with the updated effect parameters. */
  393. alAuxiliaryEffectSloti(slots[0], AL_EFFECTSLOT_EFFECT, (ALint)effects[0]);
  394. alAuxiliaryEffectSloti(slots[1], AL_EFFECTSLOT_EFFECT, (ALint)effects[1]);
  395. }
  396. int main(int argc, char **argv)
  397. {
  398. static const int MaxTransitions = 8;
  399. EFXEAXREVERBPROPERTIES reverbs[2] = {
  400. EFX_REVERB_PRESET_CARPETEDHALLWAY,
  401. EFX_REVERB_PRESET_BATHROOM
  402. };
  403. ALCdevice *device = NULL;
  404. ALCcontext *context = NULL;
  405. ALuint effects[2] = { 0, 0 };
  406. ALuint slots[2] = { 0, 0 };
  407. ALuint direct_filter = 0;
  408. ALuint buffer = 0;
  409. ALuint source = 0;
  410. ALCint num_sends = 0;
  411. ALenum state = AL_INITIAL;
  412. ALfloat direct_gain = 1.0f;
  413. int basetime = 0;
  414. int loops = 0;
  415. /* Print out usage if no arguments were specified */
  416. if(argc < 2)
  417. {
  418. fprintf(stderr, "Usage: %s [-device <name>] [options] <filename>\n\n"
  419. "Options:\n"
  420. "\t-nodirect\tSilence direct path output (easier to hear reverb)\n\n",
  421. argv[0]);
  422. return 1;
  423. }
  424. /* Initialize OpenAL, and check for EFX support with at least 2 auxiliary
  425. * sends (if multiple sends are supported, 2 are provided by default; if
  426. * you want more, you have to request it through alcCreateContext).
  427. */
  428. argv++; argc--;
  429. if(InitAL(&argv, &argc) != 0)
  430. return 1;
  431. while(argc > 0)
  432. {
  433. if(strcmp(argv[0], "-nodirect") == 0)
  434. direct_gain = 0.0f;
  435. else
  436. break;
  437. argv++;
  438. argc--;
  439. }
  440. if(argc < 1)
  441. {
  442. fprintf(stderr, "No filename specified.\n");
  443. CloseAL();
  444. return 1;
  445. }
  446. context = alcGetCurrentContext();
  447. device = alcGetContextsDevice(context);
  448. if(!alcIsExtensionPresent(device, "ALC_EXT_EFX"))
  449. {
  450. fprintf(stderr, "Error: EFX not supported\n");
  451. CloseAL();
  452. return 1;
  453. }
  454. num_sends = 0;
  455. alcGetIntegerv(device, ALC_MAX_AUXILIARY_SENDS, 1, &num_sends);
  456. if(alcGetError(device) != ALC_NO_ERROR || num_sends < 2)
  457. {
  458. fprintf(stderr, "Error: Device does not support multiple sends (got %d, need 2)\n",
  459. num_sends);
  460. CloseAL();
  461. return 1;
  462. }
  463. /* Define a macro to help load the function pointers. */
  464. #define LOAD_PROC(T, x) ((x) = FUNCTION_CAST(T, alGetProcAddress(#x)))
  465. LOAD_PROC(LPALGENFILTERS, alGenFilters);
  466. LOAD_PROC(LPALDELETEFILTERS, alDeleteFilters);
  467. LOAD_PROC(LPALISFILTER, alIsFilter);
  468. LOAD_PROC(LPALFILTERI, alFilteri);
  469. LOAD_PROC(LPALFILTERIV, alFilteriv);
  470. LOAD_PROC(LPALFILTERF, alFilterf);
  471. LOAD_PROC(LPALFILTERFV, alFilterfv);
  472. LOAD_PROC(LPALGETFILTERI, alGetFilteri);
  473. LOAD_PROC(LPALGETFILTERIV, alGetFilteriv);
  474. LOAD_PROC(LPALGETFILTERF, alGetFilterf);
  475. LOAD_PROC(LPALGETFILTERFV, alGetFilterfv);
  476. LOAD_PROC(LPALGENEFFECTS, alGenEffects);
  477. LOAD_PROC(LPALDELETEEFFECTS, alDeleteEffects);
  478. LOAD_PROC(LPALISEFFECT, alIsEffect);
  479. LOAD_PROC(LPALEFFECTI, alEffecti);
  480. LOAD_PROC(LPALEFFECTIV, alEffectiv);
  481. LOAD_PROC(LPALEFFECTF, alEffectf);
  482. LOAD_PROC(LPALEFFECTFV, alEffectfv);
  483. LOAD_PROC(LPALGETEFFECTI, alGetEffecti);
  484. LOAD_PROC(LPALGETEFFECTIV, alGetEffectiv);
  485. LOAD_PROC(LPALGETEFFECTF, alGetEffectf);
  486. LOAD_PROC(LPALGETEFFECTFV, alGetEffectfv);
  487. LOAD_PROC(LPALGENAUXILIARYEFFECTSLOTS, alGenAuxiliaryEffectSlots);
  488. LOAD_PROC(LPALDELETEAUXILIARYEFFECTSLOTS, alDeleteAuxiliaryEffectSlots);
  489. LOAD_PROC(LPALISAUXILIARYEFFECTSLOT, alIsAuxiliaryEffectSlot);
  490. LOAD_PROC(LPALAUXILIARYEFFECTSLOTI, alAuxiliaryEffectSloti);
  491. LOAD_PROC(LPALAUXILIARYEFFECTSLOTIV, alAuxiliaryEffectSlotiv);
  492. LOAD_PROC(LPALAUXILIARYEFFECTSLOTF, alAuxiliaryEffectSlotf);
  493. LOAD_PROC(LPALAUXILIARYEFFECTSLOTFV, alAuxiliaryEffectSlotfv);
  494. LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTI, alGetAuxiliaryEffectSloti);
  495. LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTIV, alGetAuxiliaryEffectSlotiv);
  496. LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf);
  497. LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv);
  498. #undef LOAD_PROC
  499. /* Load the sound into a buffer. */
  500. buffer = LoadSound(argv[0]);
  501. if(!buffer)
  502. {
  503. CloseAL();
  504. return 1;
  505. }
  506. /* Generate two effects for two "zones", and load a reverb into each one.
  507. * Note that unlike single-zone reverb, where you can store one effect per
  508. * preset, for multi-zone reverb you should have one effect per environment
  509. * instance, or one per audible zone. This is because we'll be changing the
  510. * effects' properties in real-time based on the environment instance
  511. * relative to the listener.
  512. */
  513. alGenEffects(2, effects);
  514. if(!LoadEffect(effects[0], &reverbs[0]) || !LoadEffect(effects[1], &reverbs[1]))
  515. {
  516. alDeleteEffects(2, effects);
  517. alDeleteBuffers(1, &buffer);
  518. CloseAL();
  519. return 1;
  520. }
  521. /* Create the effect slot objects, one for each "active" effect. */
  522. alGenAuxiliaryEffectSlots(2, slots);
  523. /* Tell the effect slots to use the loaded effect objects, with slot 0 for
  524. * Zone 0 and slot 1 for Zone 1. Note that this effectively copies the
  525. * effect properties. Modifying or deleting the effect object afterward
  526. * won't directly affect the effect slot until they're reapplied like this.
  527. */
  528. alAuxiliaryEffectSloti(slots[0], AL_EFFECTSLOT_EFFECT, (ALint)effects[0]);
  529. alAuxiliaryEffectSloti(slots[1], AL_EFFECTSLOT_EFFECT, (ALint)effects[1]);
  530. assert(alGetError()==AL_NO_ERROR && "Failed to set effect slot");
  531. /* For the purposes of this example, prepare a filter that optionally
  532. * silences the direct path which allows us to hear just the reverberation.
  533. * A filter like this is normally used for obstruction, where the path
  534. * directly between the listener and source is blocked (the exact
  535. * properties depending on the type and thickness of the obstructing
  536. * material).
  537. */
  538. alGenFilters(1, &direct_filter);
  539. alFilteri(direct_filter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
  540. alFilterf(direct_filter, AL_LOWPASS_GAIN, direct_gain);
  541. assert(alGetError()==AL_NO_ERROR && "Failed to set direct filter");
  542. /* Create the source to play the sound with, place it in front of the
  543. * listener's path in the left zone.
  544. */
  545. source = 0;
  546. alGenSources(1, &source);
  547. alSourcei(source, AL_LOOPING, AL_TRUE);
  548. alSource3f(source, AL_POSITION, -5.0f, 0.0f, -2.0f);
  549. alSourcei(source, AL_DIRECT_FILTER, (ALint)direct_filter);
  550. alSourcei(source, AL_BUFFER, (ALint)buffer);
  551. /* Connect the source to the effect slots. Here, we connect source send 0
  552. * to Zone 0's slot, and send 1 to Zone 1's slot. Filters can be specified
  553. * to occlude the source from each zone by varying amounts; for example, a
  554. * source within a particular zone would be unfiltered, while a source that
  555. * can only see a zone through a window or thin wall may be attenuated for
  556. * that zone.
  557. */
  558. alSource3i(source, AL_AUXILIARY_SEND_FILTER, (ALint)slots[0], 0, AL_FILTER_NULL);
  559. alSource3i(source, AL_AUXILIARY_SEND_FILTER, (ALint)slots[1], 1, AL_FILTER_NULL);
  560. assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
  561. /* Get the current time as the base for timing in the main loop. */
  562. basetime = altime_get();
  563. loops = 0;
  564. printf("Transition %d of %d...\n", loops+1, MaxTransitions);
  565. /* Play the sound for a while. */
  566. alSourcePlay(source);
  567. do {
  568. int curtime;
  569. ALfloat timediff;
  570. /* Start a batch update, to ensure all changes apply simultaneously. */
  571. alcSuspendContext(context);
  572. /* Get the current time to track the amount of time that passed.
  573. * Convert the difference to seconds.
  574. */
  575. curtime = altime_get();
  576. timediff = (float)(curtime - basetime) / 1000.0f;
  577. /* Avoid negative time deltas, in case of non-monotonic clocks. */
  578. if(timediff < 0.0f)
  579. timediff = 0.0f;
  580. else while(timediff >= 4.0f*(float)((loops&1)+1))
  581. {
  582. /* For this example, each transition occurs over 4 seconds, and
  583. * there's 2 transitions per cycle.
  584. */
  585. if(++loops < MaxTransitions)
  586. printf("Transition %d of %d...\n", loops+1, MaxTransitions);
  587. if(!(loops&1))
  588. {
  589. /* Cycle completed. Decrease the delta and increase the base
  590. * time to start a new cycle.
  591. */
  592. timediff -= 8.0f;
  593. basetime += 8000;
  594. }
  595. }
  596. /* Update the listener and effects, and finish the batch. */
  597. UpdateListenerAndEffects(timediff, slots, effects, reverbs);
  598. alcProcessContext(context);
  599. al_nssleep(10000000);
  600. alGetSourcei(source, AL_SOURCE_STATE, &state);
  601. } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING && loops < MaxTransitions);
  602. /* All done. Delete resources, and close down OpenAL. */
  603. alDeleteSources(1, &source);
  604. alDeleteAuxiliaryEffectSlots(2, slots);
  605. alDeleteEffects(2, effects);
  606. alDeleteFilters(1, &direct_filter);
  607. alDeleteBuffers(1, &buffer);
  608. CloseAL();
  609. return 0;
  610. }