alconvolve.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. /*
  2. * OpenAL Convolution Reverb Example
  3. *
  4. * Copyright (c) 2020 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 applying convolution to a source. */
  25. #include <assert.h>
  26. #include <inttypes.h>
  27. #include <limits.h>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include "sndfile.h"
  32. #include "AL/al.h"
  33. #include "AL/alext.h"
  34. #include "common/alhelpers.h"
  35. #include "win_main_utf8.h"
  36. #ifndef AL_SOFT_convolution_effect
  37. #define AL_SOFT_convolution_effect
  38. #define AL_EFFECT_CONVOLUTION_SOFT 0xA000
  39. #endif
  40. /* Filter object functions */
  41. static LPALGENFILTERS alGenFilters;
  42. static LPALDELETEFILTERS alDeleteFilters;
  43. static LPALISFILTER alIsFilter;
  44. static LPALFILTERI alFilteri;
  45. static LPALFILTERIV alFilteriv;
  46. static LPALFILTERF alFilterf;
  47. static LPALFILTERFV alFilterfv;
  48. static LPALGETFILTERI alGetFilteri;
  49. static LPALGETFILTERIV alGetFilteriv;
  50. static LPALGETFILTERF alGetFilterf;
  51. static LPALGETFILTERFV alGetFilterfv;
  52. /* Effect object functions */
  53. static LPALGENEFFECTS alGenEffects;
  54. static LPALDELETEEFFECTS alDeleteEffects;
  55. static LPALISEFFECT alIsEffect;
  56. static LPALEFFECTI alEffecti;
  57. static LPALEFFECTIV alEffectiv;
  58. static LPALEFFECTF alEffectf;
  59. static LPALEFFECTFV alEffectfv;
  60. static LPALGETEFFECTI alGetEffecti;
  61. static LPALGETEFFECTIV alGetEffectiv;
  62. static LPALGETEFFECTF alGetEffectf;
  63. static LPALGETEFFECTFV alGetEffectfv;
  64. /* Auxiliary Effect Slot object functions */
  65. static LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots;
  66. static LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots;
  67. static LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot;
  68. static LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti;
  69. static LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv;
  70. static LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf;
  71. static LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv;
  72. static LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti;
  73. static LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv;
  74. static LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf;
  75. static LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv;
  76. /* This stuff defines a simple streaming player object, the same as alstream.c.
  77. * Comments are removed for brevity, see alstream.c for more details.
  78. */
  79. enum { NumBuffers = 4 };
  80. enum { BufferSamples = 8192 };
  81. typedef struct StreamPlayer {
  82. ALuint buffers[NumBuffers];
  83. ALuint source;
  84. SNDFILE *sndfile;
  85. SF_INFO sfinfo;
  86. float *membuf;
  87. ALenum format;
  88. } StreamPlayer;
  89. static StreamPlayer *NewPlayer(void)
  90. {
  91. StreamPlayer *player;
  92. player = calloc(1, sizeof(*player));
  93. assert(player != NULL);
  94. alGenBuffers(NumBuffers, player->buffers);
  95. assert(alGetError() == AL_NO_ERROR && "Could not create buffers");
  96. alGenSources(1, &player->source);
  97. assert(alGetError() == AL_NO_ERROR && "Could not create source");
  98. alSource3i(player->source, AL_POSITION, 0, 0, -1);
  99. alSourcei(player->source, AL_SOURCE_RELATIVE, AL_TRUE);
  100. alSourcei(player->source, AL_ROLLOFF_FACTOR, 0);
  101. assert(alGetError() == AL_NO_ERROR && "Could not set source parameters");
  102. return player;
  103. }
  104. static void ClosePlayerFile(StreamPlayer *player)
  105. {
  106. if(player->sndfile)
  107. sf_close(player->sndfile);
  108. player->sndfile = NULL;
  109. free(player->membuf);
  110. player->membuf = NULL;
  111. }
  112. static void DeletePlayer(StreamPlayer *player)
  113. {
  114. ClosePlayerFile(player);
  115. alDeleteSources(1, &player->source);
  116. alDeleteBuffers(NumBuffers, player->buffers);
  117. if(alGetError() != AL_NO_ERROR)
  118. fprintf(stderr, "Failed to delete object IDs\n");
  119. memset(player, 0, sizeof(*player)); /* NOLINT(clang-analyzer-security.insecureAPI.*) */
  120. free(player);
  121. }
  122. static int OpenPlayerFile(StreamPlayer *player, const char *filename)
  123. {
  124. size_t frame_size;
  125. ClosePlayerFile(player);
  126. player->sndfile = sf_open(filename, SFM_READ, &player->sfinfo);
  127. if(!player->sndfile)
  128. {
  129. fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(NULL));
  130. return 0;
  131. }
  132. player->format = AL_NONE;
  133. if(player->sfinfo.channels == 1)
  134. player->format = AL_FORMAT_MONO_FLOAT32;
  135. else if(player->sfinfo.channels == 2)
  136. player->format = AL_FORMAT_STEREO_FLOAT32;
  137. else if(player->sfinfo.channels == 6)
  138. player->format = AL_FORMAT_51CHN32;
  139. else if(player->sfinfo.channels == 3)
  140. {
  141. if(sf_command(player->sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
  142. player->format = AL_FORMAT_BFORMAT2D_FLOAT32;
  143. }
  144. else if(player->sfinfo.channels == 4)
  145. {
  146. if(sf_command(player->sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
  147. player->format = AL_FORMAT_BFORMAT3D_FLOAT32;
  148. }
  149. if(!player->format)
  150. {
  151. fprintf(stderr, "Unsupported channel count: %d\n", player->sfinfo.channels);
  152. sf_close(player->sndfile);
  153. player->sndfile = NULL;
  154. return 0;
  155. }
  156. frame_size = (size_t)(BufferSamples * player->sfinfo.channels) * sizeof(float);
  157. player->membuf = malloc(frame_size);
  158. return 1;
  159. }
  160. static int StartPlayer(StreamPlayer *player)
  161. {
  162. ALsizei i;
  163. alSourceRewind(player->source);
  164. alSourcei(player->source, AL_BUFFER, 0);
  165. for(i = 0;i < NumBuffers;i++)
  166. {
  167. sf_count_t slen = sf_readf_float(player->sndfile, player->membuf, BufferSamples);
  168. if(slen < 1) break;
  169. slen *= player->sfinfo.channels * (sf_count_t)sizeof(float);
  170. alBufferData(player->buffers[i], player->format, player->membuf, (ALsizei)slen,
  171. player->sfinfo.samplerate);
  172. }
  173. if(alGetError() != AL_NO_ERROR)
  174. {
  175. fprintf(stderr, "Error buffering for playback\n");
  176. return 0;
  177. }
  178. alSourceQueueBuffers(player->source, i, player->buffers);
  179. alSourcePlay(player->source);
  180. if(alGetError() != AL_NO_ERROR)
  181. {
  182. fprintf(stderr, "Error starting playback\n");
  183. return 0;
  184. }
  185. return 1;
  186. }
  187. static int UpdatePlayer(StreamPlayer *player)
  188. {
  189. ALint processed, state;
  190. alGetSourcei(player->source, AL_SOURCE_STATE, &state);
  191. alGetSourcei(player->source, AL_BUFFERS_PROCESSED, &processed);
  192. if(alGetError() != AL_NO_ERROR)
  193. {
  194. fprintf(stderr, "Error checking source state\n");
  195. return 0;
  196. }
  197. while(processed > 0)
  198. {
  199. ALuint bufid;
  200. sf_count_t slen;
  201. alSourceUnqueueBuffers(player->source, 1, &bufid);
  202. processed--;
  203. slen = sf_readf_float(player->sndfile, player->membuf, BufferSamples);
  204. if(slen > 0)
  205. {
  206. slen *= player->sfinfo.channels * (sf_count_t)sizeof(float);
  207. alBufferData(bufid, player->format, player->membuf, (ALsizei)slen,
  208. player->sfinfo.samplerate);
  209. alSourceQueueBuffers(player->source, 1, &bufid);
  210. }
  211. if(alGetError() != AL_NO_ERROR)
  212. {
  213. fprintf(stderr, "Error buffering data\n");
  214. return 0;
  215. }
  216. }
  217. if(state != AL_PLAYING && state != AL_PAUSED)
  218. {
  219. ALint queued;
  220. alGetSourcei(player->source, AL_BUFFERS_QUEUED, &queued);
  221. if(queued == 0)
  222. return 0;
  223. alSourcePlay(player->source);
  224. if(alGetError() != AL_NO_ERROR)
  225. {
  226. fprintf(stderr, "Error restarting playback\n");
  227. return 0;
  228. }
  229. }
  230. return 1;
  231. }
  232. /* CreateEffect creates a new OpenAL effect object with a convolution type, and
  233. * returns the new effect ID.
  234. */
  235. static ALuint CreateEffect(void)
  236. {
  237. ALuint effect = 0;
  238. ALenum err;
  239. printf("Using Convolution\n");
  240. /* Create the effect object and set the convolution effect type. */
  241. alGenEffects(1, &effect);
  242. alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_CONVOLUTION_SOFT);
  243. /* Check if an error occurred, and clean up if so. */
  244. err = alGetError();
  245. if(err != AL_NO_ERROR)
  246. {
  247. fprintf(stderr, "OpenAL error: %s\n", alGetString(err));
  248. if(alIsEffect(effect))
  249. alDeleteEffects(1, &effect);
  250. return 0;
  251. }
  252. return effect;
  253. }
  254. /* LoadBuffer loads the named audio file into an OpenAL buffer object, and
  255. * returns the new buffer ID.
  256. */
  257. static ALuint LoadSound(const char *filename)
  258. {
  259. const char *namepart;
  260. ALenum err, format;
  261. ALuint buffer;
  262. SNDFILE *sndfile;
  263. SF_INFO sfinfo;
  264. float *membuf;
  265. sf_count_t num_frames;
  266. ALsizei num_bytes;
  267. /* Open the audio file and check that it's usable. */
  268. sndfile = sf_open(filename, SFM_READ, &sfinfo);
  269. if(!sndfile)
  270. {
  271. fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile));
  272. return 0;
  273. }
  274. if(sfinfo.frames < 1 || sfinfo.frames > (sf_count_t)(INT_MAX/sizeof(float))/sfinfo.channels)
  275. {
  276. fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames);
  277. sf_close(sndfile);
  278. return 0;
  279. }
  280. /* Get the sound format, and figure out the OpenAL format. Use floats since
  281. * impulse responses will usually have more than 16-bit precision.
  282. */
  283. format = AL_NONE;
  284. if(sfinfo.channels == 1)
  285. format = AL_FORMAT_MONO_FLOAT32;
  286. else if(sfinfo.channels == 2)
  287. format = AL_FORMAT_STEREO_FLOAT32;
  288. else if(sfinfo.channels == 3)
  289. {
  290. if(sf_command(sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
  291. format = AL_FORMAT_BFORMAT2D_FLOAT32;
  292. }
  293. else if(sfinfo.channels == 4)
  294. {
  295. if(sf_command(sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
  296. format = AL_FORMAT_BFORMAT3D_FLOAT32;
  297. }
  298. if(!format)
  299. {
  300. fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels);
  301. sf_close(sndfile);
  302. return 0;
  303. }
  304. namepart = strrchr(filename, '/');
  305. if(!namepart) namepart = strrchr(filename, '\\');
  306. if(!namepart) namepart = filename;
  307. else namepart++;
  308. printf("Loading: %s (%s, %dhz, %" PRId64 " samples / %.2f seconds)\n", namepart,
  309. FormatName(format), sfinfo.samplerate, sfinfo.frames,
  310. (double)sfinfo.frames / sfinfo.samplerate);
  311. fflush(stdout);
  312. /* Decode the whole audio file to a buffer. */
  313. membuf = malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(float));
  314. num_frames = sf_readf_float(sndfile, membuf, sfinfo.frames);
  315. if(num_frames < 1)
  316. {
  317. free(membuf);
  318. sf_close(sndfile);
  319. fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames);
  320. return 0;
  321. }
  322. num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(float);
  323. /* Buffer the audio data into a new buffer object, then free the data and
  324. * close the file.
  325. */
  326. buffer = 0;
  327. alGenBuffers(1, &buffer);
  328. alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate);
  329. free(membuf);
  330. sf_close(sndfile);
  331. /* Check if an error occurred, and clean up if so. */
  332. err = alGetError();
  333. if(err != AL_NO_ERROR)
  334. {
  335. fprintf(stderr, "OpenAL Error: %s\n", alGetString(err));
  336. if(buffer && alIsBuffer(buffer))
  337. alDeleteBuffers(1, &buffer);
  338. return 0;
  339. }
  340. return buffer;
  341. }
  342. int main(int argc, char **argv)
  343. {
  344. ALuint ir_buffer, filter, effect, slot;
  345. StreamPlayer *player;
  346. int i;
  347. /* Print out usage if no arguments were specified */
  348. if(argc < 2)
  349. {
  350. fprintf(stderr, "Usage: %s [-device <name>] <impulse response file> "
  351. "<[-dry | -nodry] filename>...\n", argv[0]);
  352. return 1;
  353. }
  354. argv++; argc--;
  355. if(InitAL(&argv, &argc) != 0)
  356. return 1;
  357. if(!alIsExtensionPresent("AL_SOFTX_convolution_effect"))
  358. {
  359. CloseAL();
  360. fprintf(stderr, "Error: Convolution effect not supported\n");
  361. return 1;
  362. }
  363. if(argc < 2)
  364. {
  365. CloseAL();
  366. fprintf(stderr, "Error: Missing impulse response or sound files\n");
  367. return 1;
  368. }
  369. /* Define a macro to help load the function pointers. */
  370. #define LOAD_PROC(T, x) ((x) = FUNCTION_CAST(T, alGetProcAddress(#x)))
  371. LOAD_PROC(LPALGENFILTERS, alGenFilters);
  372. LOAD_PROC(LPALDELETEFILTERS, alDeleteFilters);
  373. LOAD_PROC(LPALISFILTER, alIsFilter);
  374. LOAD_PROC(LPALFILTERI, alFilteri);
  375. LOAD_PROC(LPALFILTERIV, alFilteriv);
  376. LOAD_PROC(LPALFILTERF, alFilterf);
  377. LOAD_PROC(LPALFILTERFV, alFilterfv);
  378. LOAD_PROC(LPALGETFILTERI, alGetFilteri);
  379. LOAD_PROC(LPALGETFILTERIV, alGetFilteriv);
  380. LOAD_PROC(LPALGETFILTERF, alGetFilterf);
  381. LOAD_PROC(LPALGETFILTERFV, alGetFilterfv);
  382. LOAD_PROC(LPALGENEFFECTS, alGenEffects);
  383. LOAD_PROC(LPALDELETEEFFECTS, alDeleteEffects);
  384. LOAD_PROC(LPALISEFFECT, alIsEffect);
  385. LOAD_PROC(LPALEFFECTI, alEffecti);
  386. LOAD_PROC(LPALEFFECTIV, alEffectiv);
  387. LOAD_PROC(LPALEFFECTF, alEffectf);
  388. LOAD_PROC(LPALEFFECTFV, alEffectfv);
  389. LOAD_PROC(LPALGETEFFECTI, alGetEffecti);
  390. LOAD_PROC(LPALGETEFFECTIV, alGetEffectiv);
  391. LOAD_PROC(LPALGETEFFECTF, alGetEffectf);
  392. LOAD_PROC(LPALGETEFFECTFV, alGetEffectfv);
  393. LOAD_PROC(LPALGENAUXILIARYEFFECTSLOTS, alGenAuxiliaryEffectSlots);
  394. LOAD_PROC(LPALDELETEAUXILIARYEFFECTSLOTS, alDeleteAuxiliaryEffectSlots);
  395. LOAD_PROC(LPALISAUXILIARYEFFECTSLOT, alIsAuxiliaryEffectSlot);
  396. LOAD_PROC(LPALAUXILIARYEFFECTSLOTI, alAuxiliaryEffectSloti);
  397. LOAD_PROC(LPALAUXILIARYEFFECTSLOTIV, alAuxiliaryEffectSlotiv);
  398. LOAD_PROC(LPALAUXILIARYEFFECTSLOTF, alAuxiliaryEffectSlotf);
  399. LOAD_PROC(LPALAUXILIARYEFFECTSLOTFV, alAuxiliaryEffectSlotfv);
  400. LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTI, alGetAuxiliaryEffectSloti);
  401. LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTIV, alGetAuxiliaryEffectSlotiv);
  402. LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf);
  403. LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv);
  404. #undef LOAD_PROC
  405. /* Load the reverb into an effect. */
  406. effect = CreateEffect();
  407. if(!effect)
  408. {
  409. CloseAL();
  410. return 1;
  411. }
  412. /* Load the impulse response sound into a buffer. */
  413. ir_buffer = LoadSound(argv[0]);
  414. if(!ir_buffer)
  415. {
  416. alDeleteEffects(1, &effect);
  417. CloseAL();
  418. return 1;
  419. }
  420. /* Create the effect slot object. This is what "plays" an effect on sources
  421. * that connect to it.
  422. */
  423. slot = 0;
  424. alGenAuxiliaryEffectSlots(1, &slot);
  425. /* Set the impulse response sound buffer on the effect slot. This allows
  426. * effects to access it as needed. In this case, convolution uses it as the
  427. * filter source. NOTE: Unlike the effect object, the buffer *is* kept
  428. * referenced and may not be changed or deleted as long as it's set, just
  429. * like with a source. When another buffer is set, or the effect slot is
  430. * deleted, the buffer reference is released.
  431. *
  432. * The effect slot's gain is reduced because the impulse responses I've
  433. * tested with result in excessively loud reverb. Is that normal? Even with
  434. * this, it seems a bit on the loud side.
  435. *
  436. * Also note: unlike standard or EAX reverb, there is no automatic
  437. * attenuation of a source's reverb response with distance, so the reverb
  438. * will remain full volume regardless of a given sound's distance from the
  439. * listener. You can use a send filter to alter a given source's
  440. * contribution to reverb.
  441. */
  442. alAuxiliaryEffectSloti(slot, AL_BUFFER, (ALint)ir_buffer);
  443. alAuxiliaryEffectSlotf(slot, AL_EFFECTSLOT_GAIN, 1.0f / 16.0f);
  444. alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, (ALint)effect);
  445. assert(alGetError()==AL_NO_ERROR && "Failed to set effect slot");
  446. /* Create a filter that can silence the dry path. */
  447. filter = 0;
  448. alGenFilters(1, &filter);
  449. alFilteri(filter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
  450. alFilterf(filter, AL_LOWPASS_GAIN, 0.0f);
  451. player = NewPlayer();
  452. /* Connect the player's source to the effect slot. */
  453. alSource3i(player->source, AL_AUXILIARY_SEND_FILTER, (ALint)slot, 0, AL_FILTER_NULL);
  454. assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
  455. /* Play each file listed on the command line */
  456. for(i = 1;i < argc;i++)
  457. {
  458. const char *namepart;
  459. if(argc-i > 1)
  460. {
  461. if(strcasecmp(argv[i], "-nodry") == 0)
  462. {
  463. alSourcei(player->source, AL_DIRECT_FILTER, (ALint)filter);
  464. ++i;
  465. }
  466. else if(strcasecmp(argv[i], "-dry") == 0)
  467. {
  468. alSourcei(player->source, AL_DIRECT_FILTER, AL_FILTER_NULL);
  469. ++i;
  470. }
  471. }
  472. if(!OpenPlayerFile(player, argv[i]))
  473. continue;
  474. namepart = strrchr(argv[i], '/');
  475. if(!namepart) namepart = strrchr(argv[i], '\\');
  476. if(!namepart) namepart = argv[i];
  477. else namepart++;
  478. printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->format),
  479. player->sfinfo.samplerate);
  480. fflush(stdout);
  481. if(!StartPlayer(player))
  482. {
  483. ClosePlayerFile(player);
  484. continue;
  485. }
  486. while(UpdatePlayer(player))
  487. al_nssleep(10000000);
  488. ClosePlayerFile(player);
  489. }
  490. printf("Done.\n");
  491. /* All files done. Delete the player and effect resources, and close down
  492. * OpenAL.
  493. */
  494. DeletePlayer(player);
  495. player = NULL;
  496. alDeleteAuxiliaryEffectSlots(1, &slot);
  497. alDeleteEffects(1, &effect);
  498. alDeleteFilters(1, &filter);
  499. alDeleteBuffers(1, &ir_buffer);
  500. CloseAL();
  501. return 0;
  502. }