🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

302 lines
8.9 KiB

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