alloopback.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*
  2. * OpenAL Loopback Example
  3. *
  4. * Copyright (c) 2013 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 using the loopback device for custom
  25. * output handling.
  26. */
  27. #include <assert.h>
  28. #include <math.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #define SDL_MAIN_HANDLED
  33. #include "SDL3/SDL_audio.h"
  34. #include "SDL3/SDL_error.h"
  35. #include "SDL3/SDL_main.h"
  36. #include "SDL3/SDL_stdinc.h"
  37. #include "AL/al.h"
  38. #include "AL/alc.h"
  39. #include "AL/alext.h"
  40. #include "common/alhelpers.h"
  41. #ifndef M_PI
  42. #define M_PI (3.14159265358979323846)
  43. #endif
  44. typedef struct {
  45. ALCdevice *Device;
  46. ALCcontext *Context;
  47. ALCsizei FrameSize;
  48. void *Buffer;
  49. int BufferSize;
  50. } PlaybackInfo;
  51. static LPALCLOOPBACKOPENDEVICESOFT alcLoopbackOpenDeviceSOFT;
  52. static LPALCISRENDERFORMATSUPPORTEDSOFT alcIsRenderFormatSupportedSOFT;
  53. static LPALCRENDERSAMPLESSOFT alcRenderSamplesSOFT;
  54. void SDLCALL RenderSDLSamples(void *userdata, SDL_AudioStream *stream, int additional_amount,
  55. int total_amount)
  56. {
  57. PlaybackInfo *playback = (PlaybackInfo*)userdata;
  58. if(additional_amount < 0)
  59. additional_amount = total_amount;
  60. if(additional_amount <= 0)
  61. return;
  62. if(additional_amount > playback->BufferSize)
  63. {
  64. free(playback->Buffer);
  65. playback->Buffer = malloc((unsigned int)additional_amount);
  66. playback->BufferSize = additional_amount;
  67. }
  68. alcRenderSamplesSOFT(playback->Device, playback->Buffer,
  69. additional_amount/playback->FrameSize);
  70. SDL_PutAudioStreamData(stream, playback->Buffer, additional_amount);
  71. }
  72. static const char *ChannelsName(ALCenum chans)
  73. {
  74. switch(chans)
  75. {
  76. case ALC_MONO_SOFT: return "Mono";
  77. case ALC_STEREO_SOFT: return "Stereo";
  78. case ALC_QUAD_SOFT: return "Quadraphonic";
  79. case ALC_5POINT1_SOFT: return "5.1 Surround";
  80. case ALC_6POINT1_SOFT: return "6.1 Surround";
  81. case ALC_7POINT1_SOFT: return "7.1 Surround";
  82. }
  83. return "Unknown Channels";
  84. }
  85. static const char *TypeName(ALCenum type)
  86. {
  87. switch(type)
  88. {
  89. case ALC_BYTE_SOFT: return "S8";
  90. case ALC_UNSIGNED_BYTE_SOFT: return "U8";
  91. case ALC_SHORT_SOFT: return "S16";
  92. case ALC_UNSIGNED_SHORT_SOFT: return "U16";
  93. case ALC_INT_SOFT: return "S32";
  94. case ALC_UNSIGNED_INT_SOFT: return "U32";
  95. case ALC_FLOAT_SOFT: return "Float32";
  96. }
  97. return "Unknown Type";
  98. }
  99. /* Creates a one second buffer containing a sine wave, and returns the new
  100. * buffer ID. */
  101. static ALuint CreateSineWave(void)
  102. {
  103. ALshort data[44100*4];
  104. ALuint buffer;
  105. ALenum err;
  106. ALuint i;
  107. for(i = 0;i < 44100*4;i++)
  108. data[i] = (ALshort)(sin(i/44100.0 * 1000.0 * 2.0*M_PI) * 32767.0);
  109. /* Buffer the audio data into a new buffer object. */
  110. buffer = 0;
  111. alGenBuffers(1, &buffer);
  112. alBufferData(buffer, AL_FORMAT_MONO16, data, sizeof(data), 44100);
  113. /* Check if an error occurred, and clean up if so. */
  114. err = alGetError();
  115. if(err != AL_NO_ERROR)
  116. {
  117. fprintf(stderr, "OpenAL Error: %s\n", alGetString(err));
  118. if(alIsBuffer(buffer))
  119. alDeleteBuffers(1, &buffer);
  120. return 0;
  121. }
  122. return buffer;
  123. }
  124. int main(int argc, char *argv[])
  125. {
  126. PlaybackInfo playback = { NULL, NULL, 0, NULL, 0 };
  127. SDL_AudioStream *stream = NULL;
  128. SDL_AudioSpec obtained;
  129. ALuint source, buffer;
  130. ALCint attrs[16];
  131. ALenum state;
  132. (void)argc;
  133. (void)argv;
  134. SDL_SetMainReady();
  135. /* Print out error if extension is missing. */
  136. if(!alcIsExtensionPresent(NULL, "ALC_SOFT_loopback"))
  137. {
  138. fprintf(stderr, "Error: ALC_SOFT_loopback not supported!\n");
  139. return 1;
  140. }
  141. /* Define a macro to help load the function pointers. */
  142. #define LOAD_PROC(T, x) ((x) = FUNCTION_CAST(T, alcGetProcAddress(NULL, #x)))
  143. LOAD_PROC(LPALCLOOPBACKOPENDEVICESOFT, alcLoopbackOpenDeviceSOFT);
  144. LOAD_PROC(LPALCISRENDERFORMATSUPPORTEDSOFT, alcIsRenderFormatSupportedSOFT);
  145. LOAD_PROC(LPALCRENDERSAMPLESSOFT, alcRenderSamplesSOFT);
  146. #undef LOAD_PROC
  147. if(!SDL_Init(SDL_INIT_AUDIO))
  148. {
  149. fprintf(stderr, "Failed to init SDL audio: %s\n", SDL_GetError());
  150. return 1;
  151. }
  152. /* Set up SDL audio with our callback, and get the stream format. */
  153. stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL, RenderSDLSamples,
  154. &playback);
  155. if(!stream)
  156. {
  157. fprintf(stderr, "Failed to open SDL audio: %s\n", SDL_GetError());
  158. goto error;
  159. }
  160. if(!SDL_GetAudioStreamFormat(stream, &obtained, NULL))
  161. {
  162. fprintf(stderr, "Failed to query SDL audio format: %s\n", SDL_GetError());
  163. goto error;
  164. }
  165. /* Set up our OpenAL attributes based on what we got from SDL. */
  166. attrs[0] = ALC_FORMAT_CHANNELS_SOFT;
  167. if(obtained.channels == 1)
  168. attrs[1] = ALC_MONO_SOFT;
  169. else if(obtained.channels == 2)
  170. attrs[1] = ALC_STEREO_SOFT;
  171. else if(obtained.channels == 4)
  172. attrs[1] = ALC_QUAD_SOFT;
  173. else if(obtained.channels == 6)
  174. attrs[1] = ALC_5POINT1_SOFT;
  175. else if(obtained.channels == 7)
  176. attrs[1] = ALC_6POINT1_SOFT;
  177. else if(obtained.channels == 8)
  178. attrs[1] = ALC_7POINT1_SOFT;
  179. else
  180. {
  181. fprintf(stderr, "Unhandled SDL channel count: %d\n", obtained.channels);
  182. goto error;
  183. }
  184. attrs[2] = ALC_FORMAT_TYPE_SOFT;
  185. if(obtained.format == SDL_AUDIO_U8)
  186. attrs[3] = ALC_UNSIGNED_BYTE_SOFT;
  187. else if(obtained.format == SDL_AUDIO_S8)
  188. attrs[3] = ALC_BYTE_SOFT;
  189. else if(obtained.format == SDL_AUDIO_S16)
  190. attrs[3] = ALC_SHORT_SOFT;
  191. else if(obtained.format == SDL_AUDIO_S32)
  192. attrs[3] = ALC_INT_SOFT;
  193. else if(obtained.format == SDL_AUDIO_F32)
  194. attrs[3] = ALC_FLOAT_SOFT;
  195. else
  196. {
  197. fprintf(stderr, "Unhandled SDL format: 0x%04x\n", obtained.format);
  198. goto error;
  199. }
  200. attrs[4] = ALC_FREQUENCY;
  201. attrs[5] = obtained.freq;
  202. attrs[6] = 0; /* end of list */
  203. playback.FrameSize = obtained.channels * (int)SDL_AUDIO_BITSIZE(obtained.format) / 8;
  204. /* Initialize OpenAL loopback device, using our format attributes. */
  205. playback.Device = alcLoopbackOpenDeviceSOFT(NULL);
  206. if(!playback.Device)
  207. {
  208. fprintf(stderr, "Failed to open loopback device!\n");
  209. goto error;
  210. }
  211. /* Make sure the format is supported before setting them on the device. */
  212. if(alcIsRenderFormatSupportedSOFT(playback.Device, attrs[5], attrs[1], attrs[3]) == ALC_FALSE)
  213. {
  214. fprintf(stderr, "Render format not supported: %s, %s, %dhz\n",
  215. ChannelsName(attrs[1]), TypeName(attrs[3]), attrs[5]);
  216. goto error;
  217. }
  218. playback.Context = alcCreateContext(playback.Device, attrs);
  219. if(!playback.Context || alcMakeContextCurrent(playback.Context) == ALC_FALSE)
  220. {
  221. fprintf(stderr, "Failed to set an OpenAL audio context\n");
  222. goto error;
  223. }
  224. printf("Got render format from SDL stream: %s, %s, %dhz\n", ChannelsName(attrs[1]),
  225. TypeName(attrs[3]), attrs[5]);
  226. /* Start SDL playing. Our callback (thus alcRenderSamplesSOFT) will now
  227. * start being called regularly to update the AL playback state.
  228. */
  229. SDL_ResumeAudioStreamDevice(stream);
  230. /* Load the sound into a buffer. */
  231. buffer = CreateSineWave();
  232. if(!buffer)
  233. goto error;
  234. /* Create the source to play the sound with. */
  235. source = 0;
  236. alGenSources(1, &source);
  237. alSourcei(source, AL_BUFFER, (ALint)buffer);
  238. assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
  239. /* Play the sound until it finishes. */
  240. alSourcePlay(source);
  241. do {
  242. al_nssleep(10000000);
  243. alGetSourcei(source, AL_SOURCE_STATE, &state);
  244. } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING);
  245. /* All done. Delete resources, and close OpenAL. */
  246. alDeleteSources(1, &source);
  247. alDeleteBuffers(1, &buffer);
  248. /* Stop SDL playing. */
  249. SDL_PauseAudioStreamDevice(stream);
  250. /* Close up OpenAL and SDL. */
  251. SDL_DestroyAudioStream(stream);
  252. alcDestroyContext(playback.Context);
  253. alcCloseDevice(playback.Device);
  254. SDL_QuitSubSystem(SDL_INIT_AUDIO);
  255. return 0;
  256. error:
  257. if(stream)
  258. SDL_DestroyAudioStream(stream);
  259. if(playback.Context)
  260. alcDestroyContext(playback.Context);
  261. if(playback.Device)
  262. alcCloseDevice(playback.Device);
  263. SDL_QuitSubSystem(SDL_INIT_AUDIO);
  264. return 1;
  265. }