alhrtf.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /*
  2. * OpenAL HRTF Example
  3. *
  4. * Copyright (c) 2015 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 selecting an HRTF. */
  25. #include <assert.h>
  26. #include <inttypes.h>
  27. #include <limits.h>
  28. #include <math.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include "sndfile.h"
  33. #include "AL/al.h"
  34. #include "AL/alc.h"
  35. #include "AL/alext.h"
  36. #include "common/alhelpers.h"
  37. #include "win_main_utf8.h"
  38. #ifndef M_PI
  39. #define M_PI (3.14159265358979323846)
  40. #endif
  41. static LPALCGETSTRINGISOFT alcGetStringiSOFT;
  42. static LPALCRESETDEVICESOFT alcResetDeviceSOFT;
  43. /* LoadBuffer loads the named audio file into an OpenAL buffer object, and
  44. * returns the new buffer ID.
  45. */
  46. static ALuint LoadSound(const char *filename)
  47. {
  48. ALenum err, format;
  49. ALuint buffer;
  50. SNDFILE *sndfile;
  51. SF_INFO sfinfo;
  52. short *membuf;
  53. sf_count_t num_frames;
  54. ALsizei num_bytes;
  55. /* Open the audio file and check that it's usable. */
  56. sndfile = sf_open(filename, SFM_READ, &sfinfo);
  57. if(!sndfile)
  58. {
  59. fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(sndfile));
  60. return 0;
  61. }
  62. if(sfinfo.frames < 1 || sfinfo.frames > (sf_count_t)(INT_MAX/sizeof(short))/sfinfo.channels)
  63. {
  64. fprintf(stderr, "Bad sample count in %s (%" PRId64 ")\n", filename, sfinfo.frames);
  65. sf_close(sndfile);
  66. return 0;
  67. }
  68. /* Get the sound format, and figure out the OpenAL format */
  69. format = AL_NONE;
  70. if(sfinfo.channels == 1)
  71. format = AL_FORMAT_MONO16;
  72. else if(sfinfo.channels == 2)
  73. format = AL_FORMAT_STEREO16;
  74. else if(sfinfo.channels == 3)
  75. {
  76. if(sf_command(sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
  77. format = AL_FORMAT_BFORMAT2D_16;
  78. }
  79. else if(sfinfo.channels == 4)
  80. {
  81. if(sf_command(sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
  82. format = AL_FORMAT_BFORMAT3D_16;
  83. }
  84. if(!format)
  85. {
  86. fprintf(stderr, "Unsupported channel count: %d\n", sfinfo.channels);
  87. sf_close(sndfile);
  88. return 0;
  89. }
  90. /* Decode the whole audio file to a buffer. */
  91. membuf = malloc((size_t)(sfinfo.frames * sfinfo.channels) * sizeof(short));
  92. num_frames = sf_readf_short(sndfile, membuf, sfinfo.frames);
  93. if(num_frames < 1)
  94. {
  95. free(membuf);
  96. sf_close(sndfile);
  97. fprintf(stderr, "Failed to read samples in %s (%" PRId64 ")\n", filename, num_frames);
  98. return 0;
  99. }
  100. num_bytes = (ALsizei)(num_frames * sfinfo.channels) * (ALsizei)sizeof(short);
  101. /* Buffer the audio data into a new buffer object, then free the data and
  102. * close the file.
  103. */
  104. buffer = 0;
  105. alGenBuffers(1, &buffer);
  106. alBufferData(buffer, format, membuf, num_bytes, sfinfo.samplerate);
  107. free(membuf);
  108. sf_close(sndfile);
  109. /* Check if an error occurred, and clean up if so. */
  110. err = alGetError();
  111. if(err != AL_NO_ERROR)
  112. {
  113. fprintf(stderr, "OpenAL Error: %s\n", alGetString(err));
  114. if(buffer && alIsBuffer(buffer))
  115. alDeleteBuffers(1, &buffer);
  116. return 0;
  117. }
  118. return buffer;
  119. }
  120. int main(int argc, char **argv)
  121. {
  122. ALCdevice *device;
  123. ALCcontext *context;
  124. ALboolean has_angle_ext;
  125. ALuint source, buffer;
  126. const char *soundname;
  127. const char *hrtfname;
  128. ALCint hrtf_state;
  129. ALCint num_hrtf;
  130. ALdouble angle;
  131. ALenum state;
  132. /* Print out usage if no arguments were specified */
  133. if(argc < 2)
  134. {
  135. fprintf(stderr, "Usage: %s [-device <name>] [-hrtf <name>] <soundfile>\n", argv[0]);
  136. return 1;
  137. }
  138. /* Initialize OpenAL, and check for HRTF support. */
  139. argv++; argc--;
  140. if(InitAL(&argv, &argc) != 0)
  141. return 1;
  142. context = alcGetCurrentContext();
  143. device = alcGetContextsDevice(context);
  144. if(!alcIsExtensionPresent(device, "ALC_SOFT_HRTF"))
  145. {
  146. fprintf(stderr, "Error: ALC_SOFT_HRTF not supported\n");
  147. CloseAL();
  148. return 1;
  149. }
  150. /* Define a macro to help load the function pointers. */
  151. #define LOAD_PROC(d, T, x) ((x) = FUNCTION_CAST(T, alcGetProcAddress((d), #x)))
  152. LOAD_PROC(device, LPALCGETSTRINGISOFT, alcGetStringiSOFT);
  153. LOAD_PROC(device, LPALCRESETDEVICESOFT, alcResetDeviceSOFT);
  154. #undef LOAD_PROC
  155. /* Check for the AL_EXT_STEREO_ANGLES extension to be able to also rotate
  156. * stereo sources.
  157. */
  158. has_angle_ext = alIsExtensionPresent("AL_EXT_STEREO_ANGLES");
  159. printf("AL_EXT_STEREO_ANGLES %sfound\n", has_angle_ext?"":"not ");
  160. /* Check for user-preferred HRTF */
  161. if(strcmp(argv[0], "-hrtf") == 0)
  162. {
  163. hrtfname = argv[1];
  164. soundname = argv[2];
  165. }
  166. else
  167. {
  168. hrtfname = NULL;
  169. soundname = argv[0];
  170. }
  171. /* Enumerate available HRTFs, and reset the device using one. */
  172. alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);
  173. if(!num_hrtf)
  174. printf("No HRTFs found\n");
  175. else
  176. {
  177. ALCint attr[5];
  178. ALCint index = -1;
  179. ALCint i;
  180. printf("Available HRTFs:\n");
  181. for(i = 0;i < num_hrtf;i++)
  182. {
  183. const ALCchar *name = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i);
  184. printf(" %d: %s\n", i, name);
  185. /* Check if this is the HRTF the user requested. */
  186. if(hrtfname && strcmp(name, hrtfname) == 0)
  187. index = i;
  188. }
  189. i = 0;
  190. attr[i++] = ALC_HRTF_SOFT;
  191. attr[i++] = ALC_TRUE;
  192. if(index == -1)
  193. {
  194. if(hrtfname)
  195. printf("HRTF \"%s\" not found\n", hrtfname);
  196. printf("Using default HRTF...\n");
  197. }
  198. else
  199. {
  200. printf("Selecting HRTF %d...\n", index);
  201. attr[i++] = ALC_HRTF_ID_SOFT;
  202. attr[i++] = index;
  203. }
  204. attr[i] = 0;
  205. if(!alcResetDeviceSOFT(device, attr))
  206. printf("Failed to reset device: %s\n", alcGetString(device, alcGetError(device)));
  207. }
  208. /* Check if HRTF is enabled, and show which is being used. */
  209. alcGetIntegerv(device, ALC_HRTF_SOFT, 1, &hrtf_state);
  210. if(!hrtf_state)
  211. printf("HRTF not enabled!\n");
  212. else
  213. {
  214. const ALchar *name = alcGetString(device, ALC_HRTF_SPECIFIER_SOFT);
  215. printf("HRTF enabled, using %s\n", name);
  216. }
  217. fflush(stdout);
  218. /* Load the sound into a buffer. */
  219. buffer = LoadSound(soundname);
  220. if(!buffer)
  221. {
  222. CloseAL();
  223. return 1;
  224. }
  225. /* Create the source to play the sound with. */
  226. source = 0;
  227. alGenSources(1, &source);
  228. alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
  229. alSource3f(source, AL_POSITION, 0.0f, 0.0f, -1.0f);
  230. alSourcei(source, AL_BUFFER, (ALint)buffer);
  231. assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
  232. /* Play the sound until it finishes. */
  233. angle = 0.0;
  234. alSourcePlay(source);
  235. do {
  236. al_nssleep(10000000);
  237. alcSuspendContext(context);
  238. /* Rotate the source around the listener by about 1/4 cycle per second,
  239. * and keep it within -pi...+pi.
  240. */
  241. angle += 0.01 * M_PI * 0.5;
  242. if(angle > M_PI)
  243. angle -= M_PI*2.0;
  244. /* This only rotates mono sounds. */
  245. alSource3f(source, AL_POSITION, (ALfloat)sin(angle), 0.0f, -(ALfloat)cos(angle));
  246. if(has_angle_ext)
  247. {
  248. /* This rotates stereo sounds with the AL_EXT_STEREO_ANGLES
  249. * extension. Angles are specified counter-clockwise in radians.
  250. */
  251. ALfloat angles[2] = { (ALfloat)(M_PI/6.0 - angle), (ALfloat)(-M_PI/6.0 - angle) };
  252. alSourcefv(source, AL_STEREO_ANGLES, angles);
  253. }
  254. alcProcessContext(context);
  255. alGetSourcei(source, AL_SOURCE_STATE, &state);
  256. } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING);
  257. /* All done. Delete resources, and close down OpenAL. */
  258. alDeleteSources(1, &source);
  259. alDeleteBuffers(1, &buffer);
  260. CloseAL();
  261. return 0;
  262. }