🛠️🐜 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.

325 lines
9.6 KiB

  1. /*
  2. * OpenAL Audio Stream Example
  3. *
  4. * Copyright (c) 2011 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 a relatively simple streaming audio player. */
  25. #include <assert.h>
  26. #include <inttypes.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include "sndfile.h"
  31. #include "AL/al.h"
  32. #include "AL/alext.h"
  33. #include "common/alhelpers.h"
  34. /* Define the number of buffers and buffer size (in milliseconds) to use. 4
  35. * buffers with 8192 samples each gives a nice per-chunk size, and lets the
  36. * queue last for almost one second at 44.1khz. */
  37. #define NUM_BUFFERS 4
  38. #define BUFFER_SAMPLES 8192
  39. typedef struct StreamPlayer {
  40. /* These are the buffers and source to play out through OpenAL with */
  41. ALuint buffers[NUM_BUFFERS];
  42. ALuint source;
  43. /* Handle for the audio file */
  44. SNDFILE *sndfile;
  45. SF_INFO sfinfo;
  46. short *membuf;
  47. /* The format of the output stream (sample rate is in sfinfo) */
  48. ALenum format;
  49. } StreamPlayer;
  50. static StreamPlayer *NewPlayer(void);
  51. static void DeletePlayer(StreamPlayer *player);
  52. static int OpenPlayerFile(StreamPlayer *player, const char *filename);
  53. static void ClosePlayerFile(StreamPlayer *player);
  54. static int StartPlayer(StreamPlayer *player);
  55. static int UpdatePlayer(StreamPlayer *player);
  56. /* Creates a new player object, and allocates the needed OpenAL source and
  57. * buffer objects. Error checking is simplified for the purposes of this
  58. * example, and will cause an abort if needed. */
  59. static StreamPlayer *NewPlayer(void)
  60. {
  61. StreamPlayer *player;
  62. player = calloc(1, sizeof(*player));
  63. assert(player != NULL);
  64. /* Generate the buffers and source */
  65. alGenBuffers(NUM_BUFFERS, player->buffers);
  66. assert(alGetError() == AL_NO_ERROR && "Could not create buffers");
  67. alGenSources(1, &player->source);
  68. assert(alGetError() == AL_NO_ERROR && "Could not create source");
  69. /* Set parameters so mono sources play out the front-center speaker and
  70. * won't distance attenuate. */
  71. alSource3i(player->source, AL_POSITION, 0, 0, -1);
  72. alSourcei(player->source, AL_SOURCE_RELATIVE, AL_TRUE);
  73. alSourcei(player->source, AL_ROLLOFF_FACTOR, 0);
  74. assert(alGetError() == AL_NO_ERROR && "Could not set source parameters");
  75. return player;
  76. }
  77. /* Destroys a player object, deleting the source and buffers. No error handling
  78. * since these calls shouldn't fail with a properly-made player object. */
  79. static void DeletePlayer(StreamPlayer *player)
  80. {
  81. ClosePlayerFile(player);
  82. alDeleteSources(1, &player->source);
  83. alDeleteBuffers(NUM_BUFFERS, player->buffers);
  84. if(alGetError() != AL_NO_ERROR)
  85. fprintf(stderr, "Failed to delete object IDs\n");
  86. memset(player, 0, sizeof(*player));
  87. free(player);
  88. }
  89. /* Opens the first audio stream of the named file. If a file is already open,
  90. * it will be closed first. */
  91. static int OpenPlayerFile(StreamPlayer *player, const char *filename)
  92. {
  93. size_t frame_size;
  94. ClosePlayerFile(player);
  95. /* Open the audio file and check that it's usable. */
  96. player->sndfile = sf_open(filename, SFM_READ, &player->sfinfo);
  97. if(!player->sndfile)
  98. {
  99. fprintf(stderr, "Could not open audio in %s: %s\n", filename, sf_strerror(NULL));
  100. return 0;
  101. }
  102. /* Get the sound format, and figure out the OpenAL format */
  103. if(player->sfinfo.channels == 1)
  104. player->format = AL_FORMAT_MONO16;
  105. else if(player->sfinfo.channels == 2)
  106. player->format = AL_FORMAT_STEREO16;
  107. else if(player->sfinfo.channels == 3)
  108. {
  109. if(sf_command(player->sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
  110. player->format = AL_FORMAT_BFORMAT2D_16;
  111. }
  112. else if(player->sfinfo.channels == 4)
  113. {
  114. if(sf_command(player->sndfile, SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
  115. player->format = AL_FORMAT_BFORMAT3D_16;
  116. }
  117. if(!player->format)
  118. {
  119. fprintf(stderr, "Unsupported channel count: %d\n", player->sfinfo.channels);
  120. sf_close(player->sndfile);
  121. player->sndfile = NULL;
  122. return 0;
  123. }
  124. frame_size = (size_t)(BUFFER_SAMPLES * player->sfinfo.channels) * sizeof(short);
  125. player->membuf = malloc(frame_size);
  126. return 1;
  127. }
  128. /* Closes the audio file stream */
  129. static void ClosePlayerFile(StreamPlayer *player)
  130. {
  131. if(player->sndfile)
  132. sf_close(player->sndfile);
  133. player->sndfile = NULL;
  134. free(player->membuf);
  135. player->membuf = NULL;
  136. }
  137. /* Prebuffers some audio from the file, and starts playing the source */
  138. static int StartPlayer(StreamPlayer *player)
  139. {
  140. ALsizei i;
  141. /* Rewind the source position and clear the buffer queue */
  142. alSourceRewind(player->source);
  143. alSourcei(player->source, AL_BUFFER, 0);
  144. /* Fill the buffer queue */
  145. for(i = 0;i < NUM_BUFFERS;i++)
  146. {
  147. /* Get some data to give it to the buffer */
  148. sf_count_t slen = sf_readf_short(player->sndfile, player->membuf, BUFFER_SAMPLES);
  149. if(slen < 1) break;
  150. slen *= player->sfinfo.channels * (sf_count_t)sizeof(short);
  151. alBufferData(player->buffers[i], player->format, player->membuf, (ALsizei)slen,
  152. player->sfinfo.samplerate);
  153. }
  154. if(alGetError() != AL_NO_ERROR)
  155. {
  156. fprintf(stderr, "Error buffering for playback\n");
  157. return 0;
  158. }
  159. /* Now queue and start playback! */
  160. alSourceQueueBuffers(player->source, i, player->buffers);
  161. alSourcePlay(player->source);
  162. if(alGetError() != AL_NO_ERROR)
  163. {
  164. fprintf(stderr, "Error starting playback\n");
  165. return 0;
  166. }
  167. return 1;
  168. }
  169. static int UpdatePlayer(StreamPlayer *player)
  170. {
  171. ALint processed, state;
  172. /* Get relevant source info */
  173. alGetSourcei(player->source, AL_SOURCE_STATE, &state);
  174. alGetSourcei(player->source, AL_BUFFERS_PROCESSED, &processed);
  175. if(alGetError() != AL_NO_ERROR)
  176. {
  177. fprintf(stderr, "Error checking source state\n");
  178. return 0;
  179. }
  180. /* Unqueue and handle each processed buffer */
  181. while(processed > 0)
  182. {
  183. ALuint bufid;
  184. sf_count_t slen;
  185. alSourceUnqueueBuffers(player->source, 1, &bufid);
  186. processed--;
  187. /* Read the next chunk of data, refill the buffer, and queue it
  188. * back on the source */
  189. slen = sf_readf_short(player->sndfile, player->membuf, BUFFER_SAMPLES);
  190. if(slen > 0)
  191. {
  192. slen *= player->sfinfo.channels * (sf_count_t)sizeof(short);
  193. alBufferData(bufid, player->format, player->membuf, (ALsizei)slen,
  194. player->sfinfo.samplerate);
  195. alSourceQueueBuffers(player->source, 1, &bufid);
  196. }
  197. if(alGetError() != AL_NO_ERROR)
  198. {
  199. fprintf(stderr, "Error buffering data\n");
  200. return 0;
  201. }
  202. }
  203. /* Make sure the source hasn't underrun */
  204. if(state != AL_PLAYING && state != AL_PAUSED)
  205. {
  206. ALint queued;
  207. /* If no buffers are queued, playback is finished */
  208. alGetSourcei(player->source, AL_BUFFERS_QUEUED, &queued);
  209. if(queued == 0)
  210. return 0;
  211. alSourcePlay(player->source);
  212. if(alGetError() != AL_NO_ERROR)
  213. {
  214. fprintf(stderr, "Error restarting playback\n");
  215. return 0;
  216. }
  217. }
  218. return 1;
  219. }
  220. int main(int argc, char **argv)
  221. {
  222. StreamPlayer *player;
  223. int i;
  224. /* Print out usage if no arguments were specified */
  225. if(argc < 2)
  226. {
  227. fprintf(stderr, "Usage: %s [-device <name>] <filenames...>\n", argv[0]);
  228. return 1;
  229. }
  230. argv++; argc--;
  231. if(InitAL(&argv, &argc) != 0)
  232. return 1;
  233. player = NewPlayer();
  234. /* Play each file listed on the command line */
  235. for(i = 0;i < argc;i++)
  236. {
  237. const char *namepart;
  238. if(!OpenPlayerFile(player, argv[i]))
  239. continue;
  240. /* Get the name portion, without the path, for display. */
  241. namepart = strrchr(argv[i], '/');
  242. if(namepart || (namepart=strrchr(argv[i], '\\')))
  243. namepart++;
  244. else
  245. namepart = argv[i];
  246. printf("Playing: %s (%s, %dhz)\n", namepart, FormatName(player->format),
  247. player->sfinfo.samplerate);
  248. fflush(stdout);
  249. if(!StartPlayer(player))
  250. {
  251. ClosePlayerFile(player);
  252. continue;
  253. }
  254. while(UpdatePlayer(player))
  255. al_nssleep(10000000);
  256. /* All done with this file. Close it and go to the next */
  257. ClosePlayerFile(player);
  258. }
  259. printf("Done.\n");
  260. /* All files done. Delete the player, and close down OpenAL */
  261. DeletePlayer(player);
  262. player = NULL;
  263. CloseAL();
  264. return 0;
  265. }