gosu 1.1.1.1 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (219) hide show
  1. checksums.yaml +4 -4
  2. data/dependencies/SDL/include/SDL.h +108 -14
  3. data/dependencies/SDL/include/SDL_assert.h +81 -50
  4. data/dependencies/SDL/include/SDL_atomic.h +135 -35
  5. data/dependencies/SDL/include/SDL_audio.h +960 -355
  6. data/dependencies/SDL/include/SDL_bits.h +11 -6
  7. data/dependencies/SDL/include/SDL_blendmode.h +91 -14
  8. data/dependencies/SDL/include/SDL_clipboard.h +30 -7
  9. data/dependencies/SDL/include/SDL_config.h +277 -27
  10. data/dependencies/SDL/include/SDL_config_android.h +13 -38
  11. data/dependencies/SDL/include/SDL_config_iphoneos.h +21 -62
  12. data/dependencies/SDL/include/SDL_config_macosx.h +23 -92
  13. data/dependencies/SDL/include/SDL_config_minimal.h +1 -4
  14. data/dependencies/SDL/include/SDL_config_pandora.h +15 -22
  15. data/dependencies/SDL/include/SDL_config_psp.h +16 -37
  16. data/dependencies/SDL/include/SDL_config_windows.h +28 -91
  17. data/dependencies/SDL/include/SDL_config_winrt.h +33 -61
  18. data/dependencies/SDL/include/SDL_config_wiz.h +28 -56
  19. data/dependencies/SDL/include/SDL_copying.h +1 -1
  20. data/dependencies/SDL/include/SDL_cpuinfo.h +331 -71
  21. data/dependencies/SDL/include/SDL_egl.h +906 -280
  22. data/dependencies/SDL/include/SDL_endian.h +101 -47
  23. data/dependencies/SDL/include/SDL_error.h +70 -19
  24. data/dependencies/SDL/include/SDL_events.h +387 -79
  25. data/dependencies/SDL/include/SDL_filesystem.h +73 -64
  26. data/dependencies/SDL/include/SDL_gamecontroller.h +585 -125
  27. data/dependencies/SDL/include/SDL_gesture.h +36 -6
  28. data/dependencies/SDL/include/SDL_haptic.h +304 -210
  29. data/dependencies/SDL/include/SDL_hidapi.h +451 -0
  30. data/dependencies/SDL/include/SDL_hints.h +1286 -897
  31. data/dependencies/SDL/include/SDL_joystick.h +577 -130
  32. data/dependencies/SDL/include/SDL_keyboard.h +162 -63
  33. data/dependencies/SDL/include/SDL_keycode.h +7 -5
  34. data/dependencies/SDL/include/SDL_loadso.h +42 -8
  35. data/dependencies/SDL/include/SDL_locale.h +34 -32
  36. data/dependencies/SDL/include/SDL_log.h +212 -19
  37. data/dependencies/SDL/include/SDL_main.h +72 -17
  38. data/dependencies/SDL/include/SDL_messagebox.h +70 -23
  39. data/dependencies/SDL/include/SDL_metal.h +27 -32
  40. data/dependencies/SDL/include/SDL_misc.h +19 -15
  41. data/dependencies/SDL/include/SDL_mouse.h +262 -110
  42. data/dependencies/SDL/include/SDL_mutex.h +286 -66
  43. data/dependencies/SDL/include/SDL_name.h +1 -1
  44. data/dependencies/SDL/include/SDL_opengl.h +1 -1
  45. data/dependencies/SDL/include/SDL_opengles.h +1 -1
  46. data/dependencies/SDL/include/SDL_opengles2.h +2 -2
  47. data/dependencies/SDL/include/SDL_pixels.h +199 -34
  48. data/dependencies/SDL/include/SDL_platform.h +39 -2
  49. data/dependencies/SDL/include/SDL_power.h +23 -10
  50. data/dependencies/SDL/include/SDL_quit.h +1 -1
  51. data/dependencies/SDL/include/SDL_rect.h +78 -28
  52. data/dependencies/SDL/include/SDL_render.h +1204 -472
  53. data/dependencies/SDL/include/SDL_revision.h +2 -2
  54. data/dependencies/SDL/include/SDL_rwops.h +605 -33
  55. data/dependencies/SDL/include/SDL_scancode.h +1 -1
  56. data/dependencies/SDL/include/SDL_sensor.h +76 -42
  57. data/dependencies/SDL/include/SDL_shape.h +38 -27
  58. data/dependencies/SDL/include/SDL_stdinc.h +96 -24
  59. data/dependencies/SDL/include/SDL_surface.h +571 -139
  60. data/dependencies/SDL/include/SDL_system.h +339 -101
  61. data/dependencies/SDL/include/SDL_syswm.h +50 -20
  62. data/dependencies/SDL/include/SDL_test.h +1 -1
  63. data/dependencies/SDL/include/SDL_test_assert.h +2 -2
  64. data/dependencies/SDL/include/SDL_test_common.h +23 -6
  65. data/dependencies/SDL/include/SDL_test_compare.h +1 -1
  66. data/dependencies/SDL/include/SDL_test_crc32.h +1 -1
  67. data/dependencies/SDL/include/SDL_test_font.h +3 -3
  68. data/dependencies/SDL/include/SDL_test_fuzzer.h +28 -26
  69. data/dependencies/SDL/include/SDL_test_harness.h +6 -6
  70. data/dependencies/SDL/include/SDL_test_images.h +1 -1
  71. data/dependencies/SDL/include/SDL_test_log.h +1 -1
  72. data/dependencies/SDL/include/SDL_test_md5.h +1 -1
  73. data/dependencies/SDL/include/SDL_test_memory.h +1 -1
  74. data/dependencies/SDL/include/SDL_test_random.h +2 -2
  75. data/dependencies/SDL/include/SDL_thread.h +226 -128
  76. data/dependencies/SDL/include/SDL_timer.h +129 -22
  77. data/dependencies/SDL/include/SDL_touch.h +48 -8
  78. data/dependencies/SDL/include/SDL_types.h +1 -1
  79. data/dependencies/SDL/include/SDL_version.h +72 -46
  80. data/dependencies/SDL/include/SDL_video.h +1266 -460
  81. data/dependencies/SDL/include/SDL_vulkan.h +100 -161
  82. data/dependencies/SDL/include/begin_code.h +22 -1
  83. data/dependencies/SDL/include/close_code.h +1 -1
  84. data/dependencies/SDL/lib/x64/libSDL2.dll.a +0 -0
  85. data/dependencies/SDL/lib/x86/libSDL2.dll.a +0 -0
  86. data/dependencies/SDL_sound/SDL_sound.c +83 -7
  87. data/dependencies/SDL_sound/SDL_sound.h +4 -4
  88. data/dependencies/SDL_sound/SDL_sound_aiff.c +9 -12
  89. data/dependencies/SDL_sound/SDL_sound_au.c +7 -7
  90. data/dependencies/SDL_sound/SDL_sound_coreaudio.c +3 -3
  91. data/dependencies/SDL_sound/SDL_sound_flac.c +1 -1
  92. data/dependencies/SDL_sound/SDL_sound_internal.h +17 -10
  93. data/dependencies/SDL_sound/SDL_sound_modplug.c +25 -27
  94. data/dependencies/SDL_sound/SDL_sound_mp3.c +5 -17
  95. data/dependencies/SDL_sound/SDL_sound_raw.c +11 -11
  96. data/dependencies/SDL_sound/SDL_sound_shn.c +8 -7
  97. data/dependencies/SDL_sound/SDL_sound_voc.c +6 -4
  98. data/dependencies/SDL_sound/SDL_sound_vorbis.c +6 -11
  99. data/dependencies/SDL_sound/SDL_sound_wav.c +35 -29
  100. data/dependencies/SDL_sound/dr_flac.h +618 -220
  101. data/dependencies/SDL_sound/dr_mp3.h +263 -94
  102. data/dependencies/SDL_sound/libmodplug/fastmix.c +58 -64
  103. data/dependencies/SDL_sound/libmodplug/libmodplug.h +25 -103
  104. data/dependencies/SDL_sound/libmodplug/load_669.c +14 -17
  105. data/dependencies/SDL_sound/libmodplug/load_amf.c +11 -7
  106. data/dependencies/SDL_sound/libmodplug/load_ams.c +65 -22
  107. data/dependencies/SDL_sound/libmodplug/load_dbm.c +8 -4
  108. data/dependencies/SDL_sound/libmodplug/load_dmf.c +55 -25
  109. data/dependencies/SDL_sound/libmodplug/load_far.c +9 -13
  110. data/dependencies/SDL_sound/libmodplug/load_gdm.c +448 -0
  111. data/dependencies/SDL_sound/libmodplug/load_it.c +45 -49
  112. data/dependencies/SDL_sound/libmodplug/load_mdl.c +80 -53
  113. data/dependencies/SDL_sound/libmodplug/load_med.c +20 -12
  114. data/dependencies/SDL_sound/libmodplug/load_mod.c +40 -15
  115. data/dependencies/SDL_sound/libmodplug/load_mt2.c +29 -17
  116. data/dependencies/SDL_sound/libmodplug/load_okt.c +12 -8
  117. data/dependencies/SDL_sound/libmodplug/load_psm.c +101 -78
  118. data/dependencies/SDL_sound/libmodplug/load_ptm.c +18 -17
  119. data/dependencies/SDL_sound/libmodplug/load_s3m.c +9 -7
  120. data/dependencies/SDL_sound/libmodplug/load_stm.c +3 -2
  121. data/dependencies/SDL_sound/libmodplug/load_ult.c +2 -2
  122. data/dependencies/SDL_sound/libmodplug/load_umx.c +315 -35
  123. data/dependencies/SDL_sound/libmodplug/load_xm.c +25 -21
  124. data/dependencies/SDL_sound/libmodplug/mmcmp.c +295 -149
  125. data/dependencies/SDL_sound/libmodplug/modplug.c +7 -123
  126. data/dependencies/SDL_sound/libmodplug/modplug.h +32 -29
  127. data/dependencies/SDL_sound/libmodplug/snd_dsp.c +0 -1
  128. data/dependencies/SDL_sound/libmodplug/snd_flt.c +2 -2
  129. data/dependencies/SDL_sound/libmodplug/snd_fx.c +24 -18
  130. data/dependencies/SDL_sound/libmodplug/sndfile.c +55 -156
  131. data/dependencies/SDL_sound/libmodplug/sndmix.c +7 -12
  132. data/dependencies/SDL_sound/libmodplug/tables.h +10 -15
  133. data/dependencies/SDL_sound/stb_vorbis.h +508 -325
  134. data/dependencies/{al_soft → mojoAL}/AL/al.h +38 -30
  135. data/dependencies/{al_soft → mojoAL}/AL/alc.h +27 -56
  136. data/dependencies/mojoAL/mojoal.c +4594 -0
  137. data/ext/gosu/extconf.rb +29 -30
  138. data/include/Gosu/Audio.hpp +70 -85
  139. data/include/Gosu/Color.hpp +63 -107
  140. data/include/Gosu/Font.hpp +44 -48
  141. data/include/Gosu/Fwd.hpp +1 -1
  142. data/include/Gosu/Graphics.hpp +64 -75
  143. data/include/Gosu/GraphicsBase.hpp +32 -39
  144. data/include/Gosu/Image.hpp +56 -62
  145. data/include/Gosu/ImageData.hpp +23 -27
  146. data/include/Gosu/Inspection.hpp +1 -4
  147. data/include/Gosu/Math.hpp +4 -16
  148. data/include/Gosu/Platform.hpp +1 -51
  149. data/include/Gosu/Text.hpp +37 -40
  150. data/include/Gosu/TextInput.hpp +34 -40
  151. data/include/Gosu/Utility.hpp +10 -8
  152. data/include/Gosu/Version.hpp +1 -1
  153. data/include/Gosu/Window.hpp +73 -70
  154. data/lib/SDL2.dll +0 -0
  155. data/lib/gosu/compat.rb +28 -37
  156. data/lib/gosu/swig_patches.rb +31 -3
  157. data/lib/gosu.rb +2 -2
  158. data/lib64/SDL2.dll +0 -0
  159. data/rdoc/gosu.rb +9 -1
  160. data/src/Audio.cpp +88 -86
  161. data/src/AudioFile.hpp +6 -6
  162. data/src/AudioFileAudioToolbox.cpp +1 -1
  163. data/src/AudioFileSDLSound.cpp +1 -1
  164. data/src/AudioImpl.hpp +5 -5
  165. data/src/Bitmap.cpp +13 -13
  166. data/src/BitmapIO.cpp +0 -20
  167. data/src/BlockAllocator.cpp +2 -1
  168. data/src/Channel.cpp +22 -20
  169. data/src/Color.cpp +62 -55
  170. data/src/EmptyImageData.hpp +16 -18
  171. data/src/FileUnix.cpp +1 -1
  172. data/src/FileWin.cpp +1 -1
  173. data/src/Font.cpp +52 -57
  174. data/src/GosuViewController.cpp +2 -0
  175. data/src/Graphics.cpp +135 -143
  176. data/src/Image.cpp +42 -42
  177. data/src/Input.cpp +1 -1
  178. data/src/InputUIKit.cpp +1 -1
  179. data/src/LargeImageData.cpp +120 -113
  180. data/src/LargeImageData.hpp +18 -16
  181. data/src/Log.hpp +6 -6
  182. data/src/Macro.cpp +35 -37
  183. data/src/Macro.hpp +11 -11
  184. data/src/Math.cpp +8 -1
  185. data/src/RenderState.hpp +5 -5
  186. data/src/Resolution.cpp +12 -7
  187. data/src/RubyGosu.cxx +471 -502
  188. data/src/RubyGosu.h +3 -2
  189. data/src/TexChunk.cpp +50 -41
  190. data/src/TexChunk.hpp +22 -22
  191. data/src/Text.cpp +58 -59
  192. data/src/TextBuilder.cpp +60 -57
  193. data/src/TextBuilder.hpp +20 -20
  194. data/src/TextInput.cpp +127 -135
  195. data/src/TrueTypeFont.cpp +108 -108
  196. data/src/TrueTypeFont.hpp +39 -38
  197. data/src/TrueTypeFontApple.cpp +27 -23
  198. data/src/TrueTypeFontUnix.cpp +21 -26
  199. data/src/TrueTypeFontWin.cpp +30 -30
  200. data/src/Utility.cpp +82 -23
  201. data/src/WinUtility.hpp +2 -1
  202. data/src/Window.cpp +103 -86
  203. data/src/WindowUIKit.cpp +48 -51
  204. metadata +8 -20
  205. data/dependencies/SDL/include/SDL_config_os2.h +0 -188
  206. data/dependencies/SDL_sound/libmodplug/load_abc.c +0 -4725
  207. data/dependencies/SDL_sound/libmodplug/load_mid.c +0 -1405
  208. data/dependencies/SDL_sound/libmodplug/load_pat.c +0 -1143
  209. data/dependencies/SDL_sound/libmodplug/load_pat.h +0 -25
  210. data/dependencies/al_soft/AL/alext.h +0 -585
  211. data/dependencies/al_soft/AL/efx-creative.h +0 -3
  212. data/dependencies/al_soft/AL/efx-presets.h +0 -402
  213. data/dependencies/al_soft/AL/efx.h +0 -762
  214. data/dependencies/al_soft/x64/libOpenAL32.dll.a +0 -0
  215. data/dependencies/al_soft/x86/libOpenAL32.dll.a +0 -0
  216. data/lib/OpenAL32.dll +0 -0
  217. data/lib64/OpenAL32.dll +0 -0
  218. data/src/UtilityApple.cpp +0 -16
  219. data/src/UtilityWin.cpp +0 -17
@@ -0,0 +1,4594 @@
1
+ /**
2
+ * MojoAL; a simple drop-in OpenAL implementation.
3
+ *
4
+ * Please see the file LICENSE.txt in the source's root directory.
5
+ *
6
+ * This file written by Ryan C. Gordon.
7
+ */
8
+
9
+ #include <stdio.h>
10
+ #include <stdlib.h> /* needed for alloca */
11
+ #include <math.h>
12
+ #include <float.h>
13
+
14
+ #ifdef _WIN32
15
+ #define AL_API __declspec(dllexport)
16
+ #define ALC_API __declspec(dllexport)
17
+ #endif
18
+
19
+ #ifndef M_PI
20
+ #define M_PI (3.14159265358979323846264338327950288)
21
+ #endif
22
+
23
+ #include "AL/al.h"
24
+ #include "AL/alc.h"
25
+ #include "SDL.h"
26
+
27
+ #ifdef __SSE__ /* if you are on x86 or x86-64, we assume you have SSE1 by now. */
28
+ #define NEED_SCALAR_FALLBACK 0
29
+ #elif (defined(__ARM_ARCH) && (__ARM_ARCH >= 8)) /* ARMv8 always has NEON. */
30
+ #define NEED_SCALAR_FALLBACK 0
31
+ #elif (defined(__APPLE__) && defined(__ARM_ARCH) && (__ARM_ARCH >= 7)) /* All ARMv7 chips from Apple have NEON. */
32
+ #define NEED_SCALAR_FALLBACK 0
33
+ #elif (defined(__WINDOWS__) || defined(__WINRT__)) && defined(_M_ARM) /* all WinRT-level Microsoft devices have NEON */
34
+ #define NEED_SCALAR_FALLBACK 0
35
+ #else
36
+ #define NEED_SCALAR_FALLBACK 1
37
+ #endif
38
+
39
+ /* Some platforms fail to define __ARM_NEON__, others need it or arm_neon.h will fail. */
40
+ #if (defined(__ARM_ARCH) || defined(_M_ARM))
41
+ # if !NEED_SCALAR_FALLBACK && !defined(__ARM_NEON__)
42
+ # define __ARM_NEON__ 1
43
+ # endif
44
+ #endif
45
+
46
+ #ifdef __SSE__
47
+ #include <xmmintrin.h>
48
+ #endif
49
+
50
+ #ifdef __ARM_NEON__
51
+ #include <arm_neon.h>
52
+ #endif
53
+
54
+ #define OPENAL_VERSION_MAJOR 1
55
+ #define OPENAL_VERSION_MINOR 1
56
+ #define OPENAL_VERSION_STRING3(major, minor) #major "." #minor
57
+ #define OPENAL_VERSION_STRING2(major, minor) OPENAL_VERSION_STRING3(major, minor)
58
+
59
+ /* !!! FIXME: make some decisions about VENDOR and RENDERER strings here */
60
+ #define OPENAL_VERSION_STRING OPENAL_VERSION_STRING2(OPENAL_VERSION_MAJOR, OPENAL_VERSION_MINOR)
61
+ #define OPENAL_VENDOR_STRING "Ryan C. Gordon"
62
+ #define OPENAL_RENDERER_STRING "mojoAL"
63
+
64
+ #define DEFAULT_PLAYBACK_DEVICE "Default OpenAL playback device"
65
+ #define DEFAULT_CAPTURE_DEVICE "Default OpenAL capture device"
66
+
67
+ /* Number of buffers to allocate at once when we need a new block during alGenBuffers(). */
68
+ #ifndef OPENAL_BUFFER_BLOCK_SIZE
69
+ #define OPENAL_BUFFER_BLOCK_SIZE 256
70
+ #endif
71
+
72
+ /* Number of sources to allocate at once when we need a new block during alGenSources(). */
73
+ #ifndef OPENAL_SOURCE_BLOCK_SIZE
74
+ #define OPENAL_SOURCE_BLOCK_SIZE 64
75
+ #endif
76
+
77
+ /* AL_EXT_FLOAT32 support... */
78
+ #ifndef AL_FORMAT_MONO_FLOAT32
79
+ #define AL_FORMAT_MONO_FLOAT32 0x10010
80
+ #endif
81
+
82
+ #ifndef AL_FORMAT_STEREO_FLOAT32
83
+ #define AL_FORMAT_STEREO_FLOAT32 0x10011
84
+ #endif
85
+
86
+ /* ALC_EXT_DISCONNECTED support... */
87
+ #ifndef ALC_CONNECTED
88
+ #define ALC_CONNECTED 0x313
89
+ #endif
90
+
91
+
92
+ /*
93
+ The locking strategy for this OpenAL implementation:
94
+
95
+ - The initial work on this implementation attempted to be completely
96
+ lock free, and it lead to fragile, overly-clever, and complicated code.
97
+ Attempt #2 is making more reasonable tradeoffs.
98
+
99
+ - All API entry points are protected by a global mutex, which means that
100
+ calls into the API are serialized, but we expect this to not be a
101
+ serious problem; most AL calls are likely to come from a single thread
102
+ and uncontended mutexes generally aren't very expensive. This mutex
103
+ is not shared with the mixer thread, so there is never a point where
104
+ an innocent "fast" call into the AL will block because of the bad luck
105
+ of a high mixing load and the wrong moment.
106
+
107
+ - In rare cases we'll lock the mixer thread for a brief time; when a playing
108
+ source is accessible to the mixer, it is flagged as such. The mixer has a
109
+ mutex that it holds when mixing a source, and if we need to touch a source
110
+ that is flagged as accessible, we'll grab that lock to make sure there isn't
111
+ a conflict. Not all source changes need to do this. The likelihood of
112
+ hitting this case is extremely small, and the lock hold time is pretty
113
+ short. Things that might do this, only on currently-playing sources:
114
+ alDeleteSources, alSourceStop, alSourceRewind. alSourcePlay and
115
+ alSourcePause never need to lock.
116
+
117
+ - Devices are expected to live for the entire life of your OpenAL
118
+ experience, so closing one while another thread is using it is your own
119
+ fault. Don't do that. Devices are allocated pointers, and the AL doesn't
120
+ know if you've deleted it, making the pointer invalid. Device open and
121
+ close are not meant to be "fast" calls.
122
+
123
+ - Creating or destroying a context will lock the mixer thread completely
124
+ (so it isn't running _at all_ during the lock), so we can add/remove the
125
+ context on the device's list without racing. So don't do this in
126
+ time-critical code.
127
+
128
+ - Generating an object (source, buffer, etc) might need to allocate
129
+ memory, which can always take longer than you would expect. We allocate in
130
+ blocks, so not every call will allocate more memory. Generating an object
131
+ does not lock the mixer thread.
132
+
133
+ - Deleting a buffer does not lock the mixer thread (in-use buffers can
134
+ not be deleted per API spec). Deleting a source will lock the mixer briefly
135
+ if the source is still visible to the mixer. We don't believe this will be
136
+ a serious issue in normal use cases. Deleted objects' memory is marked for
137
+ reuse, but no memory is free'd by deleting sources or buffers until the
138
+ context or device, respectively, are destroyed. A deleted source that's
139
+ still visible to the mixer will not be available for reallocation until
140
+ the mixer runs another iteration, where it will mark it as no longer
141
+ visible. If you call alGenSources() during this time, a different source
142
+ will be allocated.
143
+
144
+ - alBufferData needs to allocate memory to copy new audio data. Often,
145
+ you can avoid doing these things in time-critical code. You can't set
146
+ a buffer's data when it's attached to a source (either with AL_BUFFER
147
+ or buffer queueing), so there's never a chance of contention with the
148
+ mixer thread here.
149
+
150
+ - Buffers and sources are allocated in blocks of OPENAL_BUFFER_BLOCK_SIZE
151
+ (or OPENAL_SOURCE_BLOCK_SIZE). These blocks are never deallocated as long
152
+ as the device (for buffers) or context (for sources) lives, so they don't
153
+ need a lock to access as the pointers are immutable once they're wired in.
154
+ We don't keep a ALuint name index array, but rather an array of block
155
+ pointers, which lets us find the right offset in the correct block without
156
+ iteration. The mixer thread never references the blocks directly, as they
157
+ get buffer and source pointers to objects within those blocks. Sources keep
158
+ a pointer to their specifically-bound buffer, and the mixer keeps a list of
159
+ pointers to playing sources. Since the API is serialized and the mixer
160
+ doesn't touch them, we don't need to tapdance to add new blocks.
161
+
162
+ - Buffer data is owned by the AL, and it's illegal to delete a buffer or
163
+ alBufferData() its contents while attached to a source with either
164
+ AL_BUFFER or alSourceQueueBuffers(). We keep an atomic refcount for each
165
+ buffer, and you can't change its state or delete it when its refcount is
166
+ > 0, so there isn't a race with the mixer. Refcounts only change when
167
+ changing a source's AL_BUFFER or altering its buffer queue, both of which
168
+ are protected by the api lock. The mixer thread doesn't touch the
169
+ refcount, as a buffer moving from AL_PENDING to AL_PROCESSED is still
170
+ attached to a source.
171
+
172
+ - alSource(Stop|Pause|Rewind)v with > 1 source used will always lock the
173
+ mixer thread to guarantee that all sources change in sync (!!! FIXME?).
174
+ The non-v version of these functions do not lock the mixer thread.
175
+ alSourcePlayv never locks the mixer thread (it atomically appends to a
176
+ linked list of sources to be played, which the mixer will pick up all
177
+ at once).
178
+
179
+ - alSourceQueueBuffers will build a linked list of buffers, then atomically
180
+ move this list into position for the mixer to obtain it. The mixer will
181
+ process this list without the need to be atomic (as it owns it once it
182
+ atomically claims it from from the just_queued field where
183
+ alSourceQueueBuffers staged it). As buffers are processed, the mixer moves
184
+ them atomically to a linked list that other threads can pick up for
185
+ alSourceUnqueueBuffers.
186
+
187
+ - Capture just locks the SDL audio device for everything, since it's a very
188
+ lightweight load and a much simplified API; good enough. The capture device
189
+ thread is an almost-constant minimal load (1 or 2 memcpy's, depending on the
190
+ ring buffer position), and the worst load on the API side (alcCaptureSamples)
191
+ is the same deal, so this never takes long, and is good enough.
192
+
193
+ - Probably other things. These notes might get updates later.
194
+ */
195
+
196
+ #if 1
197
+ #define FIXME(x)
198
+ #else
199
+ #define FIXME(x) { \
200
+ static int seen = 0; \
201
+ if (!seen) { \
202
+ seen = 1; \
203
+ fprintf(stderr, "FIXME: %s (%s@%s:%d)\n", x, __FUNCTION__, __FILE__, __LINE__); \
204
+ } \
205
+ }
206
+ #endif
207
+
208
+ /* restrict is from C99, but __restrict works with both Visual Studio and GCC. */
209
+ #if !defined(restrict) && ((!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901)))
210
+ #define restrict __restrict
211
+ #endif
212
+
213
+ #ifdef _MSC_VER
214
+ #define SIMDALIGNEDSTRUCT __declspec(align(16)) struct
215
+ #elif (defined(__GNUC__) || defined(__clang__))
216
+ #define SIMDALIGNEDSTRUCT struct __attribute__((aligned(16)))
217
+ #else
218
+ #define SIMDALIGNEDSTRUCT struct
219
+ #endif
220
+
221
+ #ifdef __SSE__ /* we assume you always have this on x86/x86-64 chips. SSE1 is 20 years old! */
222
+ #define has_sse 1
223
+ #endif
224
+
225
+ #ifdef __ARM_NEON__
226
+ #if NEED_SCALAR_FALLBACK
227
+ static int has_neon = 0;
228
+ #else
229
+ #define has_neon 1
230
+ #endif
231
+ #endif
232
+
233
+ static SDL_mutex *api_lock = NULL;
234
+
235
+ static int init_api_lock(void)
236
+ {
237
+ if (!api_lock) {
238
+ api_lock = SDL_CreateMutex();
239
+ if (!api_lock) {
240
+ return 0;
241
+ }
242
+ }
243
+ return 1;
244
+ }
245
+
246
+ static void grab_api_lock(void)
247
+ {
248
+ if (!api_lock) {
249
+ if (!init_api_lock()) {
250
+ return;
251
+ }
252
+ }
253
+ const int rc = SDL_LockMutex(api_lock);
254
+ SDL_assert(rc == 0);
255
+ }
256
+
257
+ static void ungrab_api_lock(void)
258
+ {
259
+ if (!api_lock) {
260
+ init_api_lock();
261
+ return;
262
+ }
263
+
264
+ const int rc = SDL_UnlockMutex(api_lock);
265
+ SDL_assert(rc == 0);
266
+ }
267
+
268
+ #define ENTRYPOINT(rettype,fn,params,args) \
269
+ rettype fn params { rettype retval; grab_api_lock(); retval = _##fn args ; ungrab_api_lock(); return retval; }
270
+
271
+ #define ENTRYPOINTVOID(fn,params,args) \
272
+ void fn params { grab_api_lock(); _##fn args ; ungrab_api_lock(); }
273
+
274
+
275
+ /* lifted this ring buffer code from my al_osx project; I wrote it all, so it's stealable. */
276
+ typedef struct
277
+ {
278
+ ALCubyte *buffer;
279
+ ALCsizei size;
280
+ ALCsizei write;
281
+ ALCsizei read;
282
+ ALCsizei used;
283
+ } RingBuffer;
284
+
285
+ static void ring_buffer_put(RingBuffer *ring, const void *_data, const ALCsizei size)
286
+ {
287
+ const ALCubyte *data = (const ALCubyte *) _data;
288
+ ALCsizei cpy;
289
+ ALCsizei avail;
290
+
291
+ if (!size) /* just in case... */
292
+ return;
293
+
294
+ /* Putting more data than ring buffer holds in total? Replace it all. */
295
+ if (size > ring->size) {
296
+ ring->write = 0;
297
+ ring->read = 0;
298
+ ring->used = ring->size;
299
+ SDL_memcpy(ring->buffer, data + (size - ring->size), ring->size);
300
+ return;
301
+ }
302
+
303
+ /* Buffer overflow? Push read pointer to oldest sample not overwritten... */
304
+ avail = ring->size - ring->used;
305
+ if (size > avail) {
306
+ ring->read += size - avail;
307
+ if (ring->read > ring->size)
308
+ ring->read -= ring->size;
309
+ }
310
+
311
+ /* Clip to end of buffer and copy first block... */
312
+ cpy = ring->size - ring->write;
313
+ if (size < cpy)
314
+ cpy = size;
315
+ if (cpy) SDL_memcpy(ring->buffer + ring->write, data, cpy);
316
+
317
+ /* Wrap around to front of ring buffer and copy remaining data... */
318
+ avail = size - cpy;
319
+ if (avail) SDL_memcpy(ring->buffer, data + cpy, avail);
320
+
321
+ /* Update write pointer... */
322
+ ring->write += size;
323
+ if (ring->write > ring->size)
324
+ ring->write -= ring->size;
325
+
326
+ ring->used += size;
327
+ if (ring->used > ring->size)
328
+ ring->used = ring->size;
329
+ }
330
+
331
+
332
+ static ALCsizei ring_buffer_get(RingBuffer *ring, void *_data, ALCsizei size)
333
+ {
334
+ ALCubyte *data = (ALCubyte *) _data;
335
+ ALCsizei cpy;
336
+ ALCsizei avail = ring->used;
337
+
338
+ /* Clamp amount to read to available data... */
339
+ if (size > avail)
340
+ size = avail;
341
+
342
+ /* Clip to end of buffer and copy first block... */
343
+ cpy = ring->size - ring->read;
344
+ if (cpy > size) cpy = size;
345
+ if (cpy) SDL_memcpy(data, ring->buffer + ring->read, cpy);
346
+
347
+ /* Wrap around to front of ring buffer and copy remaining data... */
348
+ avail = size - cpy;
349
+ if (avail) SDL_memcpy(data + cpy, ring->buffer, avail);
350
+
351
+ /* Update read pointer... */
352
+ ring->read += size;
353
+ if (ring->read > ring->size)
354
+ ring->read -= ring->size;
355
+
356
+ ring->used -= size;
357
+
358
+ return size; /* may have been clamped if there wasn't enough data... */
359
+ }
360
+
361
+ static void *calloc_simd_aligned(const size_t len)
362
+ {
363
+ Uint8 *retval = NULL;
364
+ Uint8 *ptr = (Uint8 *) SDL_calloc(1, len + 16 + sizeof (void *));
365
+ if (ptr) {
366
+ void **storeptr;
367
+ retval = ptr + sizeof (void *);
368
+ retval += 16 - (((size_t) retval) % 16);
369
+ storeptr = (void **) retval;
370
+ storeptr--;
371
+ *storeptr = ptr;
372
+ }
373
+ return retval;
374
+ }
375
+
376
+ static void free_simd_aligned(void *ptr)
377
+ {
378
+ if (ptr) {
379
+ void **realptr = (void **) ptr;
380
+ realptr--;
381
+ SDL_free(*realptr);
382
+ }
383
+ }
384
+
385
+
386
+ typedef struct ALbuffer
387
+ {
388
+ ALboolean allocated;
389
+ ALuint name;
390
+ ALint channels;
391
+ ALint bits; /* always float32 internally, but this is what alBufferData saw */
392
+ ALsizei frequency;
393
+ ALsizei len; /* length of data in bytes. */
394
+ const float *data; /* we only work in Float32 format. */
395
+ SDL_atomic_t refcount; /* if zero, can be deleted or alBufferData'd */
396
+ } ALbuffer;
397
+
398
+ /* !!! FIXME: buffers and sources use almost identical code for blocks */
399
+ typedef struct BufferBlock
400
+ {
401
+ ALbuffer buffers[OPENAL_BUFFER_BLOCK_SIZE]; /* allocate these in blocks so we can step through faster. */
402
+ ALuint used;
403
+ ALuint tmp; /* only touch under api_lock, assume it'll be gone later. */
404
+ } BufferBlock;
405
+
406
+ typedef struct BufferQueueItem
407
+ {
408
+ ALbuffer *buffer;
409
+ void *next; /* void* because we'll atomicgetptr it. */
410
+ } BufferQueueItem;
411
+
412
+ typedef struct BufferQueue
413
+ {
414
+ void *just_queued; /* void* because we'll atomicgetptr it. */
415
+ BufferQueueItem *head;
416
+ BufferQueueItem *tail;
417
+ SDL_atomic_t num_items; /* counts just_queued+head/tail */
418
+ } BufferQueue;
419
+
420
+ typedef struct ALsource ALsource;
421
+
422
+ SIMDALIGNEDSTRUCT ALsource
423
+ {
424
+ /* keep these first to help guarantee that its elements are aligned for SIMD */
425
+ ALfloat position[4];
426
+ ALfloat velocity[4];
427
+ ALfloat direction[4];
428
+ ALfloat panning[2]; /* we only do stereo for now */
429
+ SDL_atomic_t mixer_accessible;
430
+ SDL_atomic_t state; /* initial, playing, paused, stopped */
431
+ ALuint name;
432
+ ALboolean allocated;
433
+ ALenum type; /* undetermined, static, streaming */
434
+ ALboolean recalc;
435
+ ALboolean source_relative;
436
+ ALboolean looping;
437
+ ALfloat gain;
438
+ ALfloat min_gain;
439
+ ALfloat max_gain;
440
+ ALfloat reference_distance;
441
+ ALfloat max_distance;
442
+ ALfloat rolloff_factor;
443
+ ALfloat pitch;
444
+ ALfloat cone_inner_angle;
445
+ ALfloat cone_outer_angle;
446
+ ALfloat cone_outer_gain;
447
+ ALbuffer *buffer;
448
+ SDL_AudioStream *stream; /* for resampling. */
449
+ BufferQueue buffer_queue;
450
+ BufferQueue buffer_queue_processed;
451
+ ALsizei offset; /* offset in bytes for converted stream! */
452
+ ALboolean offset_latched; /* AL_SEC_OFFSET, etc, say set values apply to next alSourcePlay if not currently playing! */
453
+ ALint queue_channels;
454
+ ALsizei queue_frequency;
455
+ ALsource *playlist_next; /* linked list that contains currently-playing sources! Only touched by mixer thread! */
456
+ };
457
+
458
+ /* !!! FIXME: buffers and sources use almost identical code for blocks */
459
+ typedef struct SourceBlock
460
+ {
461
+ ALsource sources[OPENAL_SOURCE_BLOCK_SIZE]; /* allocate these in blocks so we can step through faster. */
462
+ ALuint used;
463
+ ALuint tmp; /* only touch under api_lock, assume it'll be gone later. */
464
+ } SourceBlock;
465
+
466
+
467
+ typedef struct SourcePlayTodo
468
+ {
469
+ ALsource *source;
470
+ struct SourcePlayTodo *next;
471
+ } SourcePlayTodo;
472
+
473
+ struct ALCdevice_struct
474
+ {
475
+ char *name;
476
+ ALCenum error;
477
+ SDL_atomic_t connected;
478
+ ALCboolean iscapture;
479
+ SDL_AudioDeviceID sdldevice;
480
+
481
+ ALint channels;
482
+ ALint frequency;
483
+ ALCsizei framesize;
484
+
485
+ union {
486
+ struct {
487
+ ALCcontext *contexts;
488
+ BufferBlock **buffer_blocks; /* buffers are shared between contexts on the same device. */
489
+ ALCsizei num_buffer_blocks;
490
+ BufferQueueItem *buffer_queue_pool; /* mixer thread doesn't touch this. */
491
+ void *source_todo_pool; /* void* because we'll atomicgetptr it. */
492
+ } playback;
493
+ struct {
494
+ RingBuffer ring; /* only used if iscapture */
495
+ } capture;
496
+ };
497
+ };
498
+
499
+ struct ALCcontext_struct
500
+ {
501
+ /* keep these first to help guarantee that its elements are aligned for SIMD */
502
+ SourceBlock **source_blocks;
503
+ ALsizei num_source_blocks;
504
+
505
+ SIMDALIGNEDSTRUCT {
506
+ ALfloat position[4];
507
+ ALfloat velocity[4];
508
+ ALfloat orientation[8];
509
+ ALfloat gain;
510
+ } listener;
511
+
512
+ ALCdevice *device;
513
+ SDL_atomic_t processing;
514
+ ALenum error;
515
+ ALCint *attributes;
516
+ ALCsizei attributes_count;
517
+
518
+ ALCboolean recalc;
519
+ ALenum distance_model;
520
+ ALfloat doppler_factor;
521
+ ALfloat doppler_velocity;
522
+ ALfloat speed_of_sound;
523
+
524
+ SDL_mutex *source_lock;
525
+
526
+ void *playlist_todo; /* void* so we can AtomicCASPtr it. Transmits new play commands from api thread to mixer thread */
527
+ ALsource *playlist; /* linked list of currently-playing sources. Mixer thread only! */
528
+ ALsource *playlist_tail; /* end of playlist so we know if last item is being readded. Mixer thread only! */
529
+
530
+ ALCcontext *prev; /* contexts are in a double-linked list */
531
+ ALCcontext *next;
532
+ };
533
+
534
+ /* forward declarations */
535
+ static float source_get_offset(ALsource *src, ALenum param);
536
+ static void source_set_offset(ALsource *src, ALenum param, ALfloat value);
537
+
538
+ /* the just_queued list is backwards. Add it to the queue in the correct order. */
539
+ static void queue_new_buffer_items_recursive(BufferQueue *queue, BufferQueueItem *items)
540
+ {
541
+ if (items == NULL) {
542
+ return;
543
+ }
544
+
545
+ queue_new_buffer_items_recursive(queue, items->next);
546
+ items->next = NULL;
547
+ if (queue->tail) {
548
+ queue->tail->next = items;
549
+ } else {
550
+ queue->head = items;
551
+ }
552
+ queue->tail = items;
553
+ }
554
+
555
+ static void obtain_newly_queued_buffers(BufferQueue *queue)
556
+ {
557
+ BufferQueueItem *items;
558
+ do {
559
+ items = (BufferQueueItem *) SDL_AtomicGetPtr(&queue->just_queued);
560
+ } while (!SDL_AtomicCASPtr(&queue->just_queued, items, NULL));
561
+
562
+ /* Now that we own this pointer, we can just do whatever we want with it.
563
+ Nothing touches the head/tail fields other than the mixer thread, so we
564
+ move it there. Not even atomically! :) */
565
+ SDL_assert((queue->tail != NULL) == (queue->head != NULL));
566
+
567
+ queue_new_buffer_items_recursive(queue, items);
568
+ }
569
+
570
+ /* You probably need to hold a lock before you call this (currently). */
571
+ static void source_mark_all_buffers_processed(ALsource *src)
572
+ {
573
+ obtain_newly_queued_buffers(&src->buffer_queue);
574
+ while (src->buffer_queue.head) {
575
+ void *ptr;
576
+ BufferQueueItem *item = src->buffer_queue.head;
577
+ src->buffer_queue.head = item->next;
578
+ SDL_AtomicAdd(&src->buffer_queue.num_items, -1);
579
+
580
+ /* Move it to the processed queue for alSourceUnqueueBuffers() to pick up. */
581
+ do {
582
+ ptr = SDL_AtomicGetPtr(&src->buffer_queue_processed.just_queued);
583
+ SDL_AtomicSetPtr(&item->next, ptr);
584
+ } while (!SDL_AtomicCASPtr(&src->buffer_queue_processed.just_queued, ptr, item));
585
+
586
+ SDL_AtomicAdd(&src->buffer_queue_processed.num_items, 1);
587
+ }
588
+ src->buffer_queue.tail = NULL;
589
+ }
590
+
591
+ static void source_release_buffer_queue(ALCcontext *ctx, ALsource *src)
592
+ {
593
+ /* move any buffer queue items to the device's available pool for reuse. */
594
+ obtain_newly_queued_buffers(&src->buffer_queue);
595
+ if (src->buffer_queue.tail != NULL) {
596
+ BufferQueueItem *i;
597
+ for (i = src->buffer_queue.head; i; i = i->next) {
598
+ (void) SDL_AtomicDecRef(&i->buffer->refcount);
599
+ }
600
+ src->buffer_queue.tail->next = ctx->device->playback.buffer_queue_pool;
601
+ ctx->device->playback.buffer_queue_pool = src->buffer_queue.head;
602
+ }
603
+ src->buffer_queue.head = src->buffer_queue.tail = NULL;
604
+ SDL_AtomicSet(&src->buffer_queue.num_items, 0);
605
+
606
+ obtain_newly_queued_buffers(&src->buffer_queue_processed);
607
+ if (src->buffer_queue_processed.tail != NULL) {
608
+ BufferQueueItem *i;
609
+ for (i = src->buffer_queue_processed.head; i; i = i->next) {
610
+ (void) SDL_AtomicDecRef(&i->buffer->refcount);
611
+ }
612
+ src->buffer_queue_processed.tail->next = ctx->device->playback.buffer_queue_pool;
613
+ ctx->device->playback.buffer_queue_pool = src->buffer_queue_processed.head;
614
+ }
615
+ src->buffer_queue_processed.head = src->buffer_queue_processed.tail = NULL;
616
+ SDL_AtomicSet(&src->buffer_queue_processed.num_items, 0);
617
+ }
618
+
619
+
620
+ /* ALC implementation... */
621
+
622
+ static void *current_context = NULL;
623
+ static ALCenum null_device_error = ALC_NO_ERROR;
624
+
625
+ /* we don't have any device-specific extensions. */
626
+ #define ALC_EXTENSION_ITEMS \
627
+ ALC_EXTENSION_ITEM(ALC_ENUMERATION_EXT) \
628
+ ALC_EXTENSION_ITEM(ALC_EXT_CAPTURE) \
629
+ ALC_EXTENSION_ITEM(ALC_EXT_DISCONNECT)
630
+
631
+ #define AL_EXTENSION_ITEMS \
632
+ AL_EXTENSION_ITEM(AL_EXT_FLOAT32)
633
+
634
+
635
+ static void set_alc_error(ALCdevice *device, const ALCenum error)
636
+ {
637
+ ALCenum *perr = device ? &device->error : &null_device_error;
638
+ /* can't set a new error when the previous hasn't been cleared yet. */
639
+ if (*perr == ALC_NO_ERROR) {
640
+ *perr = error;
641
+ }
642
+ }
643
+
644
+ /* all data written before the release barrier must be available before the recalc flag changes. */ \
645
+ #define context_needs_recalc(ctx) SDL_MemoryBarrierRelease(); ctx->recalc = AL_TRUE;
646
+ #define source_needs_recalc(src) SDL_MemoryBarrierRelease(); src->recalc = AL_TRUE;
647
+
648
+ static ALCdevice *prep_alc_device(const char *devicename, const ALCboolean iscapture)
649
+ {
650
+ ALCdevice *dev = NULL;
651
+
652
+ if (SDL_InitSubSystem(SDL_INIT_AUDIO) == -1) {
653
+ return NULL;
654
+ }
655
+
656
+ #ifdef __SSE__
657
+ if (!SDL_HasSSE()) {
658
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
659
+ return NULL; /* whoa! Better order a new Pentium III from Gateway 2000! */
660
+ }
661
+ #endif
662
+
663
+ #if defined(__ARM_NEON__) && !NEED_SCALAR_FALLBACK
664
+ if (!SDL_HasNEON()) {
665
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
666
+ return NULL; /* :( */
667
+ }
668
+ #elif defined(__ARM_NEON__) && NEED_SCALAR_FALLBACK
669
+ has_neon = SDL_HasNEON();
670
+ #endif
671
+
672
+ if (!init_api_lock()) {
673
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
674
+ return NULL;
675
+ }
676
+
677
+ dev = (ALCdevice *) SDL_calloc(1, sizeof (ALCdevice));
678
+ if (!dev) {
679
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
680
+ return NULL;
681
+ }
682
+
683
+ dev->name = SDL_strdup(devicename);
684
+ if (!dev->name) {
685
+ SDL_free(dev);
686
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
687
+ return NULL;
688
+ }
689
+
690
+ SDL_AtomicSet(&dev->connected, ALC_TRUE);
691
+ dev->iscapture = iscapture;
692
+
693
+ return dev;
694
+ }
695
+
696
+ /* no api lock; this creates it and otherwise doesn't have any state that can race */
697
+ ALCdevice *alcOpenDevice(const ALCchar *devicename)
698
+ {
699
+ if (!devicename) {
700
+ devicename = DEFAULT_PLAYBACK_DEVICE; /* so ALC_DEVICE_SPECIFIER is meaningful */
701
+ }
702
+
703
+ return prep_alc_device(devicename, ALC_FALSE);
704
+
705
+ /* we don't open an SDL audio device until the first context is
706
+ created, so we can attempt to match audio formats. */
707
+ }
708
+
709
+ /* no api lock; this requires you to not destroy a device that's still in use */
710
+ ALCboolean alcCloseDevice(ALCdevice *device)
711
+ {
712
+ BufferQueueItem *item;
713
+ SourcePlayTodo *todo;
714
+ ALCsizei i;
715
+
716
+ if (!device || device->iscapture) {
717
+ return ALC_FALSE;
718
+ }
719
+
720
+ /* spec: "Failure will occur if all the device's contexts and buffers have not been destroyed." */
721
+ if (device->playback.contexts) {
722
+ return ALC_FALSE;
723
+ }
724
+
725
+ for (i = 0; i <device->playback.num_buffer_blocks; i++) {
726
+ if (device->playback.buffer_blocks[i]->used > 0) {
727
+ return ALC_FALSE; /* still buffers allocated. */
728
+ }
729
+ }
730
+
731
+ if (device->sdldevice) {
732
+ SDL_CloseAudioDevice(device->sdldevice);
733
+ }
734
+
735
+ for (i = 0; i < device->playback.num_buffer_blocks; i++) {
736
+ SDL_free(device->playback.buffer_blocks[i]);
737
+ }
738
+ SDL_free(device->playback.buffer_blocks);
739
+
740
+ item = device->playback.buffer_queue_pool;
741
+ while (item) {
742
+ BufferQueueItem *next = item->next;
743
+ SDL_free(item);
744
+ item = next;
745
+ }
746
+
747
+ todo = (SourcePlayTodo *) device->playback.source_todo_pool;
748
+ while (todo) {
749
+ SourcePlayTodo *next = todo->next;
750
+ SDL_free(todo);
751
+ todo = next;
752
+ }
753
+
754
+ SDL_free(device->name);
755
+ SDL_free(device);
756
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
757
+
758
+ return ALC_TRUE;
759
+ }
760
+
761
+
762
+ static ALCboolean alcfmt_to_sdlfmt(const ALCenum alfmt, SDL_AudioFormat *sdlfmt, Uint8 *channels, ALCsizei *framesize)
763
+ {
764
+ switch (alfmt) {
765
+ case AL_FORMAT_MONO8:
766
+ *sdlfmt = AUDIO_U8;
767
+ *channels = 1;
768
+ *framesize = 1;
769
+ break;
770
+ case AL_FORMAT_MONO16:
771
+ *sdlfmt = AUDIO_S16SYS;
772
+ *channels = 1;
773
+ *framesize = 2;
774
+ break;
775
+ case AL_FORMAT_STEREO8:
776
+ *sdlfmt = AUDIO_U8;
777
+ *channels = 2;
778
+ *framesize = 2;
779
+ break;
780
+ case AL_FORMAT_STEREO16:
781
+ *sdlfmt = AUDIO_S16SYS;
782
+ *channels = 2;
783
+ *framesize = 4;
784
+ break;
785
+ case AL_FORMAT_MONO_FLOAT32:
786
+ *sdlfmt = AUDIO_F32SYS;
787
+ *channels = 1;
788
+ *framesize = 4;
789
+ break;
790
+ case AL_FORMAT_STEREO_FLOAT32:
791
+ *sdlfmt = AUDIO_F32SYS;
792
+ *channels = 2;
793
+ *framesize = 8;
794
+ break;
795
+ default:
796
+ return ALC_FALSE;
797
+ }
798
+
799
+ return ALC_TRUE;
800
+ }
801
+
802
+ static void mix_float32_c1_scalar(const ALfloat * restrict panning, const float * restrict data, float * restrict stream, const ALsizei mixframes)
803
+ {
804
+ const ALfloat left = panning[0];
805
+ const ALfloat right = panning[1];
806
+ const int unrolled = mixframes / 4;
807
+ const int leftover = mixframes % 4;
808
+ ALsizei i;
809
+
810
+ if ((left == 1.0f) && (right == 1.0f)) {
811
+ for (i = 0; i < unrolled; i++, data += 4, stream += 8) {
812
+ const float samp0 = data[0];
813
+ const float samp1 = data[1];
814
+ const float samp2 = data[2];
815
+ const float samp3 = data[3];
816
+ stream[0] += samp0;
817
+ stream[1] += samp0;
818
+ stream[2] += samp1;
819
+ stream[3] += samp1;
820
+ stream[4] += samp2;
821
+ stream[5] += samp2;
822
+ stream[6] += samp3;
823
+ stream[7] += samp3;
824
+ }
825
+ for (i = 0; i < leftover; i++, stream += 2) {
826
+ const float samp = *(data++);
827
+ stream[0] += samp;
828
+ stream[1] += samp;
829
+ }
830
+ } else {
831
+ for (i = 0; i < unrolled; i++, data += 4, stream += 8) {
832
+ const float samp0 = data[0];
833
+ const float samp1 = data[1];
834
+ const float samp2 = data[2];
835
+ const float samp3 = data[3];
836
+ stream[0] += samp0 * left;
837
+ stream[1] += samp0 * right;
838
+ stream[2] += samp1 * left;
839
+ stream[3] += samp1 * right;
840
+ stream[4] += samp2 * left;
841
+ stream[5] += samp2 * right;
842
+ stream[6] += samp3 * left;
843
+ stream[7] += samp3 * right;
844
+ }
845
+ for (i = 0; i < leftover; i++, stream += 2) {
846
+ const float samp = *(data++);
847
+ stream[0] += samp * left;
848
+ stream[1] += samp * right;
849
+ }
850
+ }
851
+ }
852
+
853
+ static void mix_float32_c2_scalar(const ALfloat * restrict panning, const float * restrict data, float * restrict stream, const ALsizei mixframes)
854
+ {
855
+ const ALfloat left = panning[0];
856
+ const ALfloat right = panning[1];
857
+ const int unrolled = mixframes / 4;
858
+ const int leftover = mixframes % 4;
859
+ ALsizei i;
860
+
861
+ if ((left == 1.0f) && (right == 1.0f)) {
862
+ for (i = 0; i < unrolled; i++, stream += 8, data += 8) {
863
+ stream[0] += data[0];
864
+ stream[1] += data[1];
865
+ stream[2] += data[2];
866
+ stream[3] += data[3];
867
+ stream[4] += data[4];
868
+ stream[5] += data[5];
869
+ stream[6] += data[6];
870
+ stream[7] += data[7];
871
+ }
872
+ for (i = 0; i < leftover; i++, stream += 2, data += 2) {
873
+ stream[0] += data[0];
874
+ stream[1] += data[1];
875
+ }
876
+ } else {
877
+ for (i = 0; i < unrolled; i++, stream += 8, data += 8) {
878
+ stream[0] += data[0] * left;
879
+ stream[1] += data[1] * right;
880
+ stream[2] += data[2] * left;
881
+ stream[3] += data[3] * right;
882
+ stream[4] += data[4] * left;
883
+ stream[5] += data[5] * right;
884
+ stream[6] += data[6] * left;
885
+ stream[7] += data[7] * right;
886
+ }
887
+ for (i = 0; i < leftover; i++, stream += 2, data += 2) {
888
+ stream[0] += data[0] * left;
889
+ stream[1] += data[1] * right;
890
+ }
891
+ }
892
+ }
893
+
894
+ #ifdef __SSE__
895
+ static void mix_float32_c1_sse(const ALfloat * restrict panning, const float * restrict data, float * restrict stream, const ALsizei mixframes)
896
+ {
897
+ const ALfloat left = panning[0];
898
+ const ALfloat right = panning[1];
899
+ const int unrolled = mixframes / 8;
900
+ const int leftover = mixframes % 8;
901
+ ALsizei i;
902
+
903
+ /* We can align this to 16 in one special case. */
904
+ if ( ((((size_t)data) % 16) == 8) && ((((size_t)stream) % 16) == 0) && (mixframes >= 2) ) {
905
+ stream[0] += data[0] * left;
906
+ stream[1] += data[0] * right;
907
+ stream[2] += data[1] * left;
908
+ stream[3] += data[1] * right;
909
+ mix_float32_c1_sse(panning, data + 2, stream + 4, mixframes - 2);
910
+ } else if ( (((size_t)stream) % 16) || (((size_t)data) % 16) ) {
911
+ /* unaligned, do scalar version. */
912
+ mix_float32_c1_scalar(panning, data, stream, mixframes);
913
+ } else if ((left == 1.0f) && (right == 1.0f)) {
914
+ for (i = 0; i < unrolled; i++, data += 8, stream += 16) {
915
+ /* We have 8 SSE registers, load 6 of them, have two for math (unrolled once). */
916
+ {
917
+ const __m128 vdataload1 = _mm_load_ps(data);
918
+ const __m128 vdataload2 = _mm_load_ps(data+4);
919
+ const __m128 vstream1 = _mm_load_ps(stream);
920
+ const __m128 vstream2 = _mm_load_ps(stream+4);
921
+ const __m128 vstream3 = _mm_load_ps(stream+8);
922
+ const __m128 vstream4 = _mm_load_ps(stream+12);
923
+ _mm_store_ps(stream, _mm_add_ps(vstream1, _mm_shuffle_ps(vdataload1, vdataload1, _MM_SHUFFLE(0, 0, 1, 1))));
924
+ _mm_store_ps(stream+4, _mm_add_ps(vstream2, _mm_shuffle_ps(vdataload1, vdataload1, _MM_SHUFFLE(2, 2, 3, 3))));
925
+ _mm_store_ps(stream+8, _mm_add_ps(vstream3, _mm_shuffle_ps(vdataload2, vdataload2, _MM_SHUFFLE(0, 0, 1, 1))));
926
+ _mm_store_ps(stream+12, _mm_add_ps(vstream4, _mm_shuffle_ps(vdataload2, vdataload2, _MM_SHUFFLE(2, 2, 3, 3))));
927
+ }
928
+ }
929
+ for (i = 0; i < leftover; i++, stream += 2) {
930
+ const float samp = *(data++);
931
+ stream[0] += samp;
932
+ stream[1] += samp;
933
+ }
934
+ } else {
935
+ const __m128 vleftright = { left, right, left, right };
936
+ for (i = 0; i < unrolled; i++, data += 8, stream += 16) {
937
+ /* We have 8 SSE registers, load 6 of them, have two for math (unrolled once). */
938
+ const __m128 vdataload1 = _mm_load_ps(data);
939
+ const __m128 vdataload2 = _mm_load_ps(data+4);
940
+ const __m128 vstream1 = _mm_load_ps(stream);
941
+ const __m128 vstream2 = _mm_load_ps(stream+4);
942
+ const __m128 vstream3 = _mm_load_ps(stream+8);
943
+ const __m128 vstream4 = _mm_load_ps(stream+12);
944
+ _mm_store_ps(stream, _mm_add_ps(vstream1, _mm_mul_ps(_mm_shuffle_ps(vdataload1, vdataload1, _MM_SHUFFLE(0, 0, 1, 1)), vleftright)));
945
+ _mm_store_ps(stream+4, _mm_add_ps(vstream2, _mm_mul_ps(_mm_shuffle_ps(vdataload1, vdataload1, _MM_SHUFFLE(2, 2, 3, 3)), vleftright)));
946
+ _mm_store_ps(stream+8, _mm_add_ps(vstream3, _mm_mul_ps(_mm_shuffle_ps(vdataload2, vdataload2, _MM_SHUFFLE(0, 0, 1, 1)), vleftright)));
947
+ _mm_store_ps(stream+12, _mm_add_ps(vstream4, _mm_mul_ps(_mm_shuffle_ps(vdataload2, vdataload2, _MM_SHUFFLE(2, 2, 3, 3)), vleftright)));
948
+ }
949
+ for (i = 0; i < leftover; i++, stream += 2) {
950
+ const float samp = *(data++);
951
+ stream[0] += samp * left;
952
+ stream[1] += samp * right;
953
+ }
954
+ }
955
+ }
956
+
957
+ static void mix_float32_c2_sse(const ALfloat * restrict panning, const float * restrict data, float * restrict stream, const ALsizei mixframes)
958
+ {
959
+ const ALfloat left = panning[0];
960
+ const ALfloat right = panning[1];
961
+ const int unrolled = mixframes / 4;
962
+ const int leftover = mixframes % 4;
963
+ ALsizei i;
964
+
965
+ /* We can align this to 16 in one special case. */
966
+ if ( ((((size_t)stream) % 16) == 8) && ((((size_t)data) % 16) == 8) && mixframes ) {
967
+ stream[0] += data[0] * left;
968
+ stream[1] += data[1] * right;
969
+ mix_float32_c2_sse(panning, data + 2, stream + 2, mixframes - 1);
970
+ } else if ( (((size_t)stream) % 16) || (((size_t)data) % 16) ) {
971
+ /* unaligned, do scalar version. */
972
+ mix_float32_c2_scalar(panning, data, stream, mixframes);
973
+ } else if ((left == 1.0f) && (right == 1.0f)) {
974
+ for (i = 0; i < unrolled; i++, data += 8, stream += 8) {
975
+ const __m128 vdata1 = _mm_load_ps(data);
976
+ const __m128 vdata2 = _mm_load_ps(data+4);
977
+ const __m128 vstream1 = _mm_load_ps(stream);
978
+ const __m128 vstream2 = _mm_load_ps(stream+4);
979
+ _mm_store_ps(stream, _mm_add_ps(vstream1, vdata1));
980
+ _mm_store_ps(stream+4, _mm_add_ps(vstream2, vdata2));
981
+ }
982
+ for (i = 0; i < leftover; i++, stream += 2, data += 2) {
983
+ stream[0] += data[0];
984
+ stream[1] += data[1];
985
+ }
986
+ } else {
987
+ const __m128 vleftright = { left, right, left, right };
988
+ for (i = 0; i < unrolled; i++, data += 8, stream += 8) {
989
+ const __m128 vdata1 = _mm_load_ps(data);
990
+ const __m128 vdata2 = _mm_load_ps(data+4);
991
+ const __m128 vstream1 = _mm_load_ps(stream);
992
+ const __m128 vstream2 = _mm_load_ps(stream+4);
993
+ _mm_store_ps(stream, _mm_add_ps(vstream1, _mm_mul_ps(vdata1, vleftright)));
994
+ _mm_store_ps(stream+4, _mm_add_ps(vstream2, _mm_mul_ps(vdata2, vleftright)));
995
+ }
996
+ for (i = 0; i < leftover; i++, stream += 2, data += 2) {
997
+ stream[0] += data[0] * left;
998
+ stream[1] += data[1] * right;
999
+ }
1000
+ }
1001
+ }
1002
+ #endif
1003
+
1004
+ #ifdef __ARM_NEON__
1005
+ static void mix_float32_c1_neon(const ALfloat * restrict panning, const float * restrict data, float * restrict stream, const ALsizei mixframes)
1006
+ {
1007
+ const ALfloat left = panning[0];
1008
+ const ALfloat right = panning[1];
1009
+ const int unrolled = mixframes / 8;
1010
+ const int leftover = mixframes % 8;
1011
+ ALsizei i;
1012
+
1013
+ /* We can align this to 16 in one special case. */
1014
+ if ( ((((size_t)data) % 16) == 8) && ((((size_t)stream) % 16) == 0) && (mixframes >= 2) ) {
1015
+ stream[0] += data[0] * left;
1016
+ stream[1] += data[0] * right;
1017
+ stream[2] += data[1] * left;
1018
+ stream[3] += data[1] * right;
1019
+ mix_float32_c1_neon(panning, data + 2, stream + 4, mixframes - 2);
1020
+ } else if ( (((size_t)stream) % 16) || (((size_t)data) % 16) ) {
1021
+ /* unaligned, do scalar version. */
1022
+ mix_float32_c1_scalar(panning, data, stream, mixframes);
1023
+ } else if ((left == 1.0f) && (right == 1.0f)) {
1024
+ for (i = 0; i < unrolled; i++, data += 8, stream += 16) {
1025
+ const float32x4_t vdataload1 = vld1q_f32(data);
1026
+ const float32x4_t vdataload2 = vld1q_f32(data+4);
1027
+ const float32x4_t vstream1 = vld1q_f32(stream);
1028
+ const float32x4_t vstream2 = vld1q_f32(stream+4);
1029
+ const float32x4_t vstream3 = vld1q_f32(stream+8);
1030
+ const float32x4_t vstream4 = vld1q_f32(stream+12);
1031
+ const float32x4x2_t vzipped1 = vzipq_f32(vdataload1, vdataload1);
1032
+ const float32x4x2_t vzipped2 = vzipq_f32(vdataload2, vdataload2);
1033
+ vst1q_f32(stream, vaddq_f32(vstream1, vzipped1.val[0]));
1034
+ vst1q_f32(stream+4, vaddq_f32(vstream2, vzipped1.val[1]));
1035
+ vst1q_f32(stream+8, vaddq_f32(vstream3, vzipped2.val[0]));
1036
+ vst1q_f32(stream+12, vaddq_f32(vstream4, vzipped2.val[1]));
1037
+ }
1038
+ for (i = 0; i < leftover; i++, stream += 2) {
1039
+ const float samp = *(data++);
1040
+ stream[0] += samp;
1041
+ stream[1] += samp;
1042
+ }
1043
+ } else {
1044
+ const float32x4_t vleftright = { left, right, left, right };
1045
+ for (i = 0; i < unrolled; i++, data += 8, stream += 16) {
1046
+ const float32x4_t vdataload1 = vld1q_f32(data);
1047
+ const float32x4_t vdataload2 = vld1q_f32(data+4);
1048
+ const float32x4_t vstream1 = vld1q_f32(stream);
1049
+ const float32x4_t vstream2 = vld1q_f32(stream+4);
1050
+ const float32x4_t vstream3 = vld1q_f32(stream+8);
1051
+ const float32x4_t vstream4 = vld1q_f32(stream+12);
1052
+ const float32x4x2_t vzipped1 = vzipq_f32(vdataload1, vdataload1);
1053
+ const float32x4x2_t vzipped2 = vzipq_f32(vdataload2, vdataload2);
1054
+ vst1q_f32(stream, vmlaq_f32(vstream1, vzipped1.val[0], vleftright));
1055
+ vst1q_f32(stream+4, vmlaq_f32(vstream2, vzipped1.val[1], vleftright));
1056
+ vst1q_f32(stream+8, vmlaq_f32(vstream3, vzipped2.val[0], vleftright));
1057
+ vst1q_f32(stream+12, vmlaq_f32(vstream4, vzipped2.val[1], vleftright));
1058
+ }
1059
+ for (i = 0; i < leftover; i++, stream += 2) {
1060
+ const float samp = *(data++);
1061
+ stream[0] += samp * left;
1062
+ stream[1] += samp * right;
1063
+ }
1064
+ }
1065
+ }
1066
+
1067
+ static void mix_float32_c2_neon(const ALfloat * restrict panning, const float * restrict data, float * restrict stream, const ALsizei mixframes)
1068
+ {
1069
+ const ALfloat left = panning[0];
1070
+ const ALfloat right = panning[1];
1071
+ const int unrolled = mixframes / 8;
1072
+ const int leftover = mixframes % 8;
1073
+ ALsizei i;
1074
+
1075
+ /* We can align this to 16 in one special case. */
1076
+ if ( ((((size_t)stream) % 16) == 8) && ((((size_t)data) % 16) == 8) && mixframes ) {
1077
+ stream[0] += data[0] * left;
1078
+ stream[1] += data[1] * right;
1079
+ mix_float32_c2_neon(panning, data + 2, stream + 2, mixframes - 1);
1080
+ } else if ( (((size_t)stream) % 16) || (((size_t)data) % 16) ) {
1081
+ /* unaligned, do scalar version. */
1082
+ mix_float32_c2_scalar(panning, data, stream, mixframes);
1083
+ } else if ((left == 1.0f) && (right == 1.0f)) {
1084
+ for (i = 0; i < unrolled; i++, data += 16, stream += 16) {
1085
+ const float32x4_t vdata1 = vld1q_f32(data);
1086
+ const float32x4_t vdata2 = vld1q_f32(data+4);
1087
+ const float32x4_t vdata3 = vld1q_f32(data+8);
1088
+ const float32x4_t vdata4 = vld1q_f32(data+12);
1089
+ const float32x4_t vstream1 = vld1q_f32(stream);
1090
+ const float32x4_t vstream2 = vld1q_f32(stream+4);
1091
+ const float32x4_t vstream3 = vld1q_f32(stream+8);
1092
+ const float32x4_t vstream4 = vld1q_f32(stream+12);
1093
+ vst1q_f32(stream, vaddq_f32(vstream1, vdata1));
1094
+ vst1q_f32(stream+4, vaddq_f32(vstream2, vdata2));
1095
+ vst1q_f32(stream+8, vaddq_f32(vstream3, vdata3));
1096
+ vst1q_f32(stream+12, vaddq_f32(vstream4, vdata4));
1097
+ }
1098
+ for (i = 0; i < leftover; i++, stream += 2, data += 2) {
1099
+ stream[0] += data[0];
1100
+ stream[1] += data[1];
1101
+ }
1102
+ } else {
1103
+ const float32x4_t vleftright = { left, right, left, right };
1104
+ for (i = 0; i < unrolled; i++, data += 16, stream += 16) {
1105
+ const float32x4_t vdata1 = vld1q_f32(data);
1106
+ const float32x4_t vdata2 = vld1q_f32(data+4);
1107
+ const float32x4_t vdata3 = vld1q_f32(data+8);
1108
+ const float32x4_t vdata4 = vld1q_f32(data+12);
1109
+ const float32x4_t vstream1 = vld1q_f32(stream);
1110
+ const float32x4_t vstream2 = vld1q_f32(stream+4);
1111
+ const float32x4_t vstream3 = vld1q_f32(stream+8);
1112
+ const float32x4_t vstream4 = vld1q_f32(stream+12);
1113
+ vst1q_f32(stream, vmlaq_f32(vstream1, vdata1, vleftright));
1114
+ vst1q_f32(stream+4, vmlaq_f32(vstream2, vdata2, vleftright));
1115
+ vst1q_f32(stream+8, vmlaq_f32(vstream3, vdata3, vleftright));
1116
+ vst1q_f32(stream+12, vmlaq_f32(vstream4, vdata4, vleftright));
1117
+ }
1118
+ for (i = 0; i < leftover; i++, stream += 2, data += 2) {
1119
+ stream[0] += data[0] * left;
1120
+ stream[1] += data[1] * right;
1121
+ }
1122
+ }
1123
+ }
1124
+ #endif
1125
+
1126
+
1127
+ static void mix_buffer(const ALbuffer *buffer, const ALfloat * restrict panning, const float * restrict data, float * restrict stream, const ALsizei mixframes)
1128
+ {
1129
+ const ALfloat left = panning[0];
1130
+ const ALfloat right = panning[1];
1131
+ FIXME("currently expects output to be stereo");
1132
+ if ((left != 0.0f) || (right != 0.0f)) { /* don't bother mixing in silence. */
1133
+ if (buffer->channels == 1) {
1134
+ #ifdef __SSE__
1135
+ if (has_sse) { mix_float32_c1_sse(panning, data, stream, mixframes); } else
1136
+ #elif defined(__ARM_NEON__)
1137
+ if (has_neon) { mix_float32_c1_neon(panning, data, stream, mixframes); } else
1138
+ #endif
1139
+ {
1140
+ #if NEED_SCALAR_FALLBACK
1141
+ mix_float32_c1_scalar(panning, data, stream, mixframes);
1142
+ #else
1143
+ SDL_assert(!"uhoh, we didn't compile in enough mixers!");
1144
+ #endif
1145
+ }
1146
+ } else {
1147
+ SDL_assert(buffer->channels == 2);
1148
+ #ifdef __SSE__
1149
+ if (has_sse) { mix_float32_c2_sse(panning, data, stream, mixframes); } else
1150
+ #elif defined(__ARM_NEON__)
1151
+ if (has_neon) { mix_float32_c2_neon(panning, data, stream, mixframes); } else
1152
+ #endif
1153
+ {
1154
+ #if NEED_SCALAR_FALLBACK
1155
+ mix_float32_c2_scalar(panning, data, stream, mixframes);
1156
+ #else
1157
+ SDL_assert(!"uhoh, we didn't compile in enough mixers!");
1158
+ #endif
1159
+ }
1160
+ }
1161
+ }
1162
+ }
1163
+
1164
+ static ALboolean mix_source_buffer(ALCcontext *ctx, ALsource *src, BufferQueueItem *queue, float **stream, int *len)
1165
+ {
1166
+ const ALbuffer *buffer = queue ? queue->buffer : NULL;
1167
+ ALboolean processed = AL_TRUE;
1168
+
1169
+ /* you can legally queue or set a NULL buffer. */
1170
+ if (buffer && buffer->data && (buffer->len > 0)) {
1171
+ const float *data = buffer->data + (src->offset / sizeof (float));
1172
+ const int bufferframesize = (int) (buffer->channels * sizeof (float));
1173
+ const int deviceframesize = ctx->device->framesize;
1174
+ const int framesneeded = *len / deviceframesize;
1175
+
1176
+ SDL_assert(src->offset < buffer->len);
1177
+
1178
+ if (src->stream) { /* resampling? */
1179
+ int mixframes, mixlen, remainingmixframes;
1180
+ while ( (((mixlen = SDL_AudioStreamAvailable(src->stream)) / bufferframesize) < framesneeded) && (src->offset < buffer->len) ) {
1181
+ const int framesput = (buffer->len - src->offset) / bufferframesize;
1182
+ const int bytesput = SDL_min(framesput, 1024) * bufferframesize;
1183
+ FIXME("dynamically adjust frames here?"); /* we hardcode 1024 samples when opening the audio device, too. */
1184
+ SDL_AudioStreamPut(src->stream, data, bytesput);
1185
+ src->offset += bytesput;
1186
+ data += bytesput / sizeof (float);
1187
+ }
1188
+
1189
+ mixframes = SDL_min(mixlen / bufferframesize, framesneeded);
1190
+ remainingmixframes = mixframes;
1191
+ while (remainingmixframes > 0) {
1192
+ float mixbuf[256];
1193
+ const int mixbuflen = sizeof (mixbuf);
1194
+ const int mixbufframes = mixbuflen / bufferframesize;
1195
+ const int getframes = SDL_min(remainingmixframes, mixbufframes);
1196
+ SDL_AudioStreamGet(src->stream, mixbuf, getframes * bufferframesize);
1197
+ mix_buffer(buffer, src->panning, mixbuf, *stream, getframes);
1198
+ *len -= getframes * deviceframesize;
1199
+ *stream += getframes * ctx->device->channels;
1200
+ remainingmixframes -= getframes;
1201
+ }
1202
+ } else {
1203
+ const int framesavail = (buffer->len - src->offset) / bufferframesize;
1204
+ const int mixframes = SDL_min(framesneeded, framesavail);
1205
+ mix_buffer(buffer, src->panning, data, *stream, mixframes);
1206
+ src->offset += mixframes * bufferframesize;
1207
+ *len -= mixframes * deviceframesize;
1208
+ *stream += mixframes * ctx->device->channels;
1209
+ }
1210
+
1211
+ SDL_assert(src->offset <= buffer->len);
1212
+
1213
+ processed = src->offset >= buffer->len;
1214
+ if (processed) {
1215
+ FIXME("does the offset have to represent the whole queue or just the current buffer?");
1216
+ src->offset = 0;
1217
+ }
1218
+ }
1219
+
1220
+ return processed;
1221
+ }
1222
+
1223
+ static ALCboolean mix_source_buffer_queue(ALCcontext *ctx, ALsource *src, BufferQueueItem *queue, float *stream, int len)
1224
+ {
1225
+ ALCboolean keep = ALC_TRUE;
1226
+
1227
+ while ((len > 0) && (mix_source_buffer(ctx, src, queue, &stream, &len))) {
1228
+ /* Finished this buffer! */
1229
+ BufferQueueItem *item = queue;
1230
+ BufferQueueItem *next = queue ? queue->next : NULL;
1231
+ void *ptr;
1232
+
1233
+ if (queue) {
1234
+ queue->next = NULL;
1235
+ queue = next;
1236
+ }
1237
+
1238
+ SDL_assert((src->type == AL_STATIC) || (src->type == AL_STREAMING));
1239
+ if (src->type == AL_STREAMING) { /* mark buffer processed. */
1240
+ SDL_assert(item == src->buffer_queue.head);
1241
+ FIXME("bubble out all these NULL checks"); /* these are only here because we check for looping/stopping in this loop, but we really shouldn't enter this loop at all if queue==NULL. */
1242
+ if (item != NULL) {
1243
+ src->buffer_queue.head = next;
1244
+ if (!next) {
1245
+ src->buffer_queue.tail = NULL;
1246
+ }
1247
+ SDL_AtomicAdd(&src->buffer_queue.num_items, -1);
1248
+
1249
+ /* Move it to the processed queue for alSourceUnqueueBuffers() to pick up. */
1250
+ do {
1251
+ ptr = SDL_AtomicGetPtr(&src->buffer_queue_processed.just_queued);
1252
+ SDL_AtomicSetPtr(&item->next, ptr);
1253
+ } while (!SDL_AtomicCASPtr(&src->buffer_queue_processed.just_queued, ptr, item));
1254
+
1255
+ SDL_AtomicAdd(&src->buffer_queue_processed.num_items, 1);
1256
+ }
1257
+ }
1258
+
1259
+ if (queue == NULL) { /* nothing else to play? */
1260
+ if (src->looping) {
1261
+ FIXME("looping is supposed to move to AL_INITIAL then immediately to AL_PLAYING, but I'm not sure what side effect this is meant to trigger");
1262
+ if (src->type == AL_STREAMING) {
1263
+ FIXME("what does looping do with the AL_STREAMING state?");
1264
+ }
1265
+ } else {
1266
+ SDL_AtomicSet(&src->state, AL_STOPPED);
1267
+ keep = ALC_FALSE;
1268
+ }
1269
+ break; /* nothing else to mix here, so stop. */
1270
+ }
1271
+ }
1272
+
1273
+ return keep;
1274
+ }
1275
+
1276
+ /* All the 3D math here is way overcommented because I HAVE NO IDEA WHAT I'M
1277
+ DOING and had to research the hell out of what are probably pretty simple
1278
+ concepts. Pay attention in math class, kids. */
1279
+
1280
+ /* The scalar versions have explanitory comments and links. The SIMD versions don't. */
1281
+
1282
+ /* calculates cross product. https://en.wikipedia.org/wiki/Cross_product
1283
+ Basically takes two vectors and gives you a vector that's perpendicular
1284
+ to both.
1285
+ */
1286
+ #if NEED_SCALAR_FALLBACK
1287
+ static void xyzzy(ALfloat *v, const ALfloat *a, const ALfloat *b)
1288
+ {
1289
+ v[0] = (a[1] * b[2]) - (a[2] * b[1]);
1290
+ v[1] = (a[2] * b[0]) - (a[0] * b[2]);
1291
+ v[2] = (a[0] * b[1]) - (a[1] * b[0]);
1292
+ }
1293
+
1294
+ /* calculate dot product (multiply each element of two vectors, sum them) */
1295
+ static ALfloat dotproduct(const ALfloat *a, const ALfloat *b)
1296
+ {
1297
+ return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]);
1298
+ }
1299
+
1300
+ /* calculate distance ("magnitude") in 3D space:
1301
+ https://math.stackexchange.com/questions/42640/calculate-distance-in-3d-space
1302
+ assumes vector starts at (0,0,0). */
1303
+ static ALfloat magnitude(const ALfloat *v)
1304
+ {
1305
+ /* technically, the inital part on this is just a dot product of itself. */
1306
+ return SDL_sqrtf((v[0] * v[0]) + (v[1] * v[1]) + (v[2] * v[2]));
1307
+ }
1308
+
1309
+ /* https://www.khanacademy.org/computing/computer-programming/programming-natural-simulations/programming-vectors/a/vector-magnitude-normalization */
1310
+ static void normalize(ALfloat *v)
1311
+ {
1312
+ const ALfloat mag = magnitude(v);
1313
+ if (mag == 0.0f) {
1314
+ SDL_memset(v, '\0', sizeof (*v) * 3);
1315
+ } else {
1316
+ v[0] /= mag;
1317
+ v[1] /= mag;
1318
+ v[2] /= mag;
1319
+ }
1320
+ }
1321
+ #endif
1322
+
1323
+ #ifdef __SSE__
1324
+ static __m128 xyzzy_sse(const __m128 a, const __m128 b)
1325
+ {
1326
+ /* http://fastcpp.blogspot.com/2011/04/vector-cross-product-using-sse-code.html
1327
+ this is the "three shuffle" version in the comments, plus the variables swapped around for handedness in the later comment. */
1328
+ const __m128 v = _mm_sub_ps(
1329
+ _mm_mul_ps(a, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1))),
1330
+ _mm_mul_ps(b, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)))
1331
+ );
1332
+ return _mm_shuffle_ps(v, v, _MM_SHUFFLE(3, 0, 2, 1));
1333
+ }
1334
+
1335
+ static ALfloat dotproduct_sse(const __m128 a, const __m128 b)
1336
+ {
1337
+ const __m128 prod = _mm_mul_ps(a, b);
1338
+ const __m128 sum1 = _mm_add_ps(prod, _mm_shuffle_ps(prod, prod, _MM_SHUFFLE(1, 0, 3, 2)));
1339
+ const __m128 sum2 = _mm_add_ps(sum1, _mm_shuffle_ps(sum1, sum1, _MM_SHUFFLE(2, 2, 0, 0)));
1340
+ FIXME("this can use _mm_hadd_ps in SSE3, or _mm_dp_ps in SSE4.1");
1341
+ return _mm_cvtss_f32(_mm_shuffle_ps(sum2, sum2, _MM_SHUFFLE(3, 3, 3, 3)));
1342
+ }
1343
+
1344
+ static ALfloat magnitude_sse(const __m128 v)
1345
+ {
1346
+ return SDL_sqrtf(dotproduct_sse(v, v));
1347
+ }
1348
+
1349
+ static __m128 normalize_sse(const __m128 v)
1350
+ {
1351
+ const ALfloat mag = magnitude_sse(v);
1352
+ if (mag == 0.0f) {
1353
+ return _mm_setzero_ps();
1354
+ }
1355
+ return _mm_div_ps(v, _mm_set_ps1(mag));
1356
+ }
1357
+ #endif
1358
+
1359
+ #ifdef __ARM_NEON__
1360
+ static float32x4_t xyzzy_neon(const float32x4_t a, const float32x4_t b)
1361
+ {
1362
+ const float32x4_t shuf_a = { a[1], a[2], a[0], a[3] };
1363
+ const float32x4_t shuf_b = { b[1], b[2], b[0], b[3] };
1364
+ const float32x4_t v = vsubq_f32(vmulq_f32(a, shuf_b), vmulq_f32(b, shuf_a));
1365
+ const float32x4_t retval = { v[1], v[2], v[0], v[3] };
1366
+ FIXME("need a better permute");
1367
+ return retval;
1368
+ }
1369
+
1370
+ static ALfloat dotproduct_neon(const float32x4_t a, const float32x4_t b)
1371
+ {
1372
+ const float32x4_t prod = vmulq_f32(a, b);
1373
+ const float32x4_t sum1 = vaddq_f32(prod, vrev64q_f32(prod));
1374
+ const float32x4_t sum2 = vaddq_f32(sum1, vcombine_f32(vget_high_f32(sum1), vget_low_f32(sum1)));
1375
+ return sum2[3];
1376
+ }
1377
+
1378
+ static ALfloat magnitude_neon(const float32x4_t v)
1379
+ {
1380
+ return SDL_sqrtf(dotproduct_neon(v, v));
1381
+ }
1382
+
1383
+ static float32x4_t normalize_neon(const float32x4_t v)
1384
+ {
1385
+ const ALfloat mag = magnitude_neon(v);
1386
+ if (mag == 0.0f) {
1387
+ return vdupq_n_f32(0.0f);
1388
+ }
1389
+ return vmulq_f32(v, vdupq_n_f32(1.0f / mag));
1390
+ }
1391
+ #endif
1392
+
1393
+
1394
+
1395
+ /* Get the sin(angle) and cos(angle) at the same time. Ideally, with one
1396
+ instruction, like what is offered on the x86.
1397
+ angle is in radians, not degrees. */
1398
+ static void calculate_sincos(const ALfloat angle, ALfloat *_sin, ALfloat *_cos)
1399
+ {
1400
+ *_sin = SDL_sinf(angle);
1401
+ *_cos = SDL_cosf(angle);
1402
+ }
1403
+
1404
+ static ALfloat calculate_distance_attenuation(const ALCcontext *ctx, const ALsource *src, ALfloat distance)
1405
+ {
1406
+ /* AL SPEC: "With all the distance models, if the formula can not be
1407
+ evaluated then the source will not be attenuated. For example, if a
1408
+ linear model is being used with AL_REFERENCE_DISTANCE equal to
1409
+ AL_MAX_DISTANCE, then the gain equation will have a divide-by-zero
1410
+ error in it. In this case, there is no attenuation for that source." */
1411
+ FIXME("check divisions by zero");
1412
+
1413
+ switch (ctx->distance_model) {
1414
+ case AL_INVERSE_DISTANCE_CLAMPED:
1415
+ distance = SDL_min(SDL_max(distance, src->reference_distance), src->max_distance);
1416
+ /* fallthrough */
1417
+ case AL_INVERSE_DISTANCE:
1418
+ /* AL SPEC: "gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE + AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE))" */
1419
+ return src->reference_distance / (src->reference_distance + src->rolloff_factor * (distance - src->reference_distance));
1420
+
1421
+ case AL_LINEAR_DISTANCE_CLAMPED:
1422
+ distance = SDL_max(distance, src->reference_distance);
1423
+ /* fallthrough */
1424
+ case AL_LINEAR_DISTANCE:
1425
+ /* AL SPEC: "distance = min(distance, AL_MAX_DISTANCE) // avoid negative gain
1426
+ gain = (1 - AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE) / (AL_MAX_DISTANCE - AL_REFERENCE_DISTANCE))" */
1427
+ return 1.0f - src->rolloff_factor * (SDL_min(distance, src->max_distance) - src->reference_distance) / (src->max_distance - src->reference_distance);
1428
+
1429
+ case AL_EXPONENT_DISTANCE_CLAMPED:
1430
+ distance = SDL_min(SDL_max(distance, src->reference_distance), src->max_distance);
1431
+ /* fallthrough */
1432
+ case AL_EXPONENT_DISTANCE:
1433
+ /* AL SPEC: "gain = (distance / AL_REFERENCE_DISTANCE) ^ (- AL_ROLLOFF_FACTOR)" */
1434
+ return SDL_powf(distance / src->reference_distance, -src->rolloff_factor);
1435
+
1436
+ default: break;
1437
+ }
1438
+
1439
+ SDL_assert(!"Unexpected distance model");
1440
+ return 1.0f;
1441
+ }
1442
+
1443
+ static void calculate_channel_gains(const ALCcontext *ctx, const ALsource *src, float *gains)
1444
+ {
1445
+ /* rolloff==0.0f makes all distance models result in 1.0f,
1446
+ and we never spatialize non-mono sources, per the AL spec. */
1447
+ const ALboolean spatialize = (ctx->distance_model != AL_NONE) &&
1448
+ (src->queue_channels == 1) &&
1449
+ (src->rolloff_factor != 0.0f);
1450
+
1451
+ const ALfloat *at = &ctx->listener.orientation[0];
1452
+ const ALfloat *up = &ctx->listener.orientation[4];
1453
+
1454
+ ALfloat distance;
1455
+ ALfloat gain;
1456
+ ALfloat radians;
1457
+
1458
+ #ifdef __SSE__
1459
+ __m128 position_sse;
1460
+ #elif defined(__ARM_NEON__)
1461
+ float32x4_t position_neon = vdupq_n_f32(0.0f);
1462
+ #endif
1463
+
1464
+ #if NEED_SCALAR_FALLBACK
1465
+ ALfloat position[3];
1466
+ #endif
1467
+
1468
+ /* this goes through the steps the AL spec dictates for gain and distance attenuation... */
1469
+
1470
+ if (!spatialize) {
1471
+ /* simpler path through the same AL spec details if not spatializing. */
1472
+ gain = SDL_min(SDL_max(src->gain, src->min_gain), src->max_gain) * ctx->listener.gain;
1473
+ gains[0] = gains[1] = gain; /* no spatialization, but AL_GAIN (etc) is still applied. */
1474
+ return;
1475
+ }
1476
+
1477
+ #ifdef __SSE__
1478
+ if (has_sse) {
1479
+ position_sse = _mm_load_ps(src->position);
1480
+ if (!src->source_relative) {
1481
+ position_sse = _mm_sub_ps(position_sse, _mm_load_ps(ctx->listener.position));
1482
+ }
1483
+ distance = magnitude_sse(position_sse);
1484
+ } else
1485
+ #elif defined(__ARM_NEON__)
1486
+ if (has_neon) {
1487
+ position_neon = vld1q_f32(src->position);
1488
+ if (!src->source_relative) {
1489
+ position_neon = vsubq_f32(position_neon, vld1q_f32(ctx->listener.position));
1490
+ }
1491
+ distance = magnitude_neon(position_neon);
1492
+ } else
1493
+ #endif
1494
+
1495
+ {
1496
+ #if NEED_SCALAR_FALLBACK
1497
+ SDL_memcpy(position, src->position, sizeof (position));
1498
+ /* if values aren't source-relative, then convert it to be so. */
1499
+ if (!src->source_relative) {
1500
+ position[0] -= ctx->listener.position[0];
1501
+ position[1] -= ctx->listener.position[1];
1502
+ position[2] -= ctx->listener.position[2];
1503
+ }
1504
+ distance = magnitude(position);
1505
+ #endif
1506
+ }
1507
+
1508
+ /* AL SPEC: ""1. Distance attenuation is calculated first, including
1509
+ minimum (AL_REFERENCE_DISTANCE) and maximum (AL_MAX_DISTANCE)
1510
+ thresholds." */
1511
+ gain = calculate_distance_attenuation(ctx, src, distance);
1512
+
1513
+ /* AL SPEC: "2. The result is then multiplied by source gain (AL_GAIN)." */
1514
+ gain *= src->gain;
1515
+
1516
+ /* AL SPEC: "3. If the source is directional (AL_CONE_INNER_ANGLE less
1517
+ than AL_CONE_OUTER_ANGLE), an angle-dependent attenuation is calculated
1518
+ depending on AL_CONE_OUTER_GAIN, and multiplied with the distance
1519
+ dependent attenuation. The resulting attenuation factor for the given
1520
+ angle and distance between listener and source is multiplied with
1521
+ source AL_GAIN." */
1522
+ if (src->cone_inner_angle < src->cone_outer_angle) {
1523
+ FIXME("directional sources");
1524
+ }
1525
+
1526
+ /* AL SPEC: "4. The effective gain computed this way is compared against
1527
+ AL_MIN_GAIN and AL_MAX_GAIN thresholds." */
1528
+ gain = SDL_min(SDL_max(gain, src->min_gain), src->max_gain);
1529
+
1530
+ /* AL SPEC: "5. The result is guaranteed to be clamped to [AL_MIN_GAIN,
1531
+ AL_MAX_GAIN], and subsequently multiplied by listener gain which serves
1532
+ as an overall volume control. The implementation is free to clamp
1533
+ listener gain if necessary due to hardware or implementation
1534
+ constraints." */
1535
+ gain *= ctx->listener.gain;
1536
+
1537
+ /* now figure out positioning. Since we're aiming for stereo, we just
1538
+ need a simple panning effect. We're going to do what's called
1539
+ "constant power panning," as explained...
1540
+
1541
+ https://dsp.stackexchange.com/questions/21691/algorithm-to-pan-audio
1542
+
1543
+ Naturally, we'll need to know the angle between where our listener
1544
+ is facing and where the source is to make that work...
1545
+
1546
+ https://www.youtube.com/watch?v=S_568VZWFJo
1547
+
1548
+ ...but to do that, we need to rotate so we have the correct side of
1549
+ the listener, which isn't just a point in space, but has a definite
1550
+ direction it is facing. More or less, this is what gluLookAt deals
1551
+ with...
1552
+
1553
+ http://www.songho.ca/opengl/gl_camera.html
1554
+
1555
+ ...although I messed with the algorithm until it did what I wanted.
1556
+
1557
+ XYZZY!! https://en.wikipedia.org/wiki/Cross_product#Mnemonic
1558
+ */
1559
+
1560
+ #ifdef __SSE__ /* (the math is explained in the scalar version.) */
1561
+ if (has_sse) {
1562
+ const __m128 at_sse = _mm_load_ps(at);
1563
+ const __m128 U_sse = normalize_sse(xyzzy_sse(at_sse, _mm_load_ps(up)));
1564
+ const __m128 V_sse = xyzzy_sse(at_sse, U_sse);
1565
+ const __m128 N_sse = normalize_sse(at_sse);
1566
+ const __m128 rotated_sse = {
1567
+ dotproduct_sse(position_sse, U_sse),
1568
+ -dotproduct_sse(position_sse, V_sse),
1569
+ -dotproduct_sse(position_sse, N_sse),
1570
+ 0.0f
1571
+ };
1572
+
1573
+ const ALfloat mags = magnitude_sse(at_sse) * magnitude_sse(rotated_sse);
1574
+ radians = (mags == 0.0f) ? 0.0f : SDL_acosf(dotproduct_sse(at_sse, rotated_sse) / mags);
1575
+ if (_mm_comilt_ss(rotated_sse, _mm_setzero_ps())) {
1576
+ radians = -radians;
1577
+ }
1578
+ } else
1579
+ #endif
1580
+
1581
+ #ifdef __ARM_NEON__ /* (the math is explained in the scalar version.) */
1582
+ if (has_neon) {
1583
+ const float32x4_t at_neon = vld1q_f32(at);
1584
+ const float32x4_t U_neon = normalize_neon(xyzzy_neon(at_neon, vld1q_f32(up)));
1585
+ const float32x4_t V_neon = xyzzy_neon(at_neon, U_neon);
1586
+ const float32x4_t N_neon = normalize_neon(at_neon);
1587
+ const float32x4_t rotated_neon = {
1588
+ dotproduct_neon(position_neon, U_neon),
1589
+ -dotproduct_neon(position_neon, V_neon),
1590
+ -dotproduct_neon(position_neon, N_neon),
1591
+ 0.0f
1592
+ };
1593
+
1594
+ const ALfloat mags = magnitude_neon(at_neon) * magnitude_neon(rotated_neon);
1595
+ radians = (mags == 0.0f) ? 0.0f : SDL_acosf(dotproduct_neon(at_neon, rotated_neon) / mags);
1596
+ if (rotated_neon[0] < 0.0f) {
1597
+ radians = -radians;
1598
+ }
1599
+ } else
1600
+ #endif
1601
+
1602
+ {
1603
+ #if NEED_SCALAR_FALLBACK
1604
+ ALfloat U[3];
1605
+ ALfloat V[3];
1606
+ ALfloat N[3];
1607
+ ALfloat rotated[3];
1608
+ ALfloat mags;
1609
+
1610
+ xyzzy(U, at, up);
1611
+ normalize(U);
1612
+ xyzzy(V, at, U);
1613
+ SDL_memcpy(N, at, sizeof (N));
1614
+ normalize(N);
1615
+
1616
+ /* we don't need the bottom row of the gluLookAt matrix, since we don't
1617
+ translate. (Matrix * Vector) is just filling in each element of the
1618
+ output vector with the dot product of a row of the matrix and the
1619
+ vector. I made some of these negative to make it work for my purposes,
1620
+ but that's not what GLU does here.
1621
+
1622
+ (This says gluLookAt is left-handed, so maybe that's part of it?)
1623
+ https://stackoverflow.com/questions/25933581/how-u-v-n-camera-coordinate-system-explained-with-opengl
1624
+ */
1625
+ rotated[0] = dotproduct(position, U);
1626
+ rotated[1] = -dotproduct(position, V);
1627
+ rotated[2] = -dotproduct(position, N);
1628
+
1629
+ /* At this point, we have rotated vector and we can calculate the angle
1630
+ from 0 (directly in front of where the listener is facing) to 180
1631
+ degrees (directly behind) ... */
1632
+
1633
+ mags = magnitude(at) * magnitude(rotated);
1634
+ radians = (mags == 0.0f) ? 0.0f : SDL_acosf(dotproduct(at, rotated) / mags);
1635
+ /* and we already have what we need to decide if those degrees are on the
1636
+ listener's left or right...
1637
+ https://gamedev.stackexchange.com/questions/43897/determining-if-something-is-on-the-right-or-left-side-of-an-object
1638
+ ...we already did this dot product: it's in rotated[0]. */
1639
+
1640
+ /* make it negative to the left, positive to the right. */
1641
+ if (rotated[0] < 0.0f) {
1642
+ radians = -radians;
1643
+ }
1644
+ #endif
1645
+ }
1646
+
1647
+ /* here comes the Constant Power Panning magic... */
1648
+ #define SQRT2_DIV2 0.7071067812f /* sqrt(2.0) / 2.0 ... */
1649
+
1650
+ /* this might be a terrible idea, which is totally my own doing here,
1651
+ but here you go: Constant Power Panning only works from -45 to 45
1652
+ degrees in front of the listener. So we split this into 4 quadrants.
1653
+ - from -45 to 45: standard panning.
1654
+ - from 45 to 135: pan full right.
1655
+ - from 135 to 225: flip angle so it works like standard panning.
1656
+ - from 225 to -45: pan full left. */
1657
+
1658
+ #define RADIANS_45_DEGREES 0.7853981634f
1659
+ #define RADIANS_135_DEGREES 2.3561944902f
1660
+ if ((radians >= -RADIANS_45_DEGREES) && (radians <= RADIANS_45_DEGREES)) {
1661
+ ALfloat sine, cosine;
1662
+ calculate_sincos(radians, &sine, &cosine);
1663
+ gains[0] = (SQRT2_DIV2 * (cosine - sine));
1664
+ gains[1] = (SQRT2_DIV2 * (cosine + sine));
1665
+ } else if ((radians >= RADIANS_45_DEGREES) && (radians <= RADIANS_135_DEGREES)) {
1666
+ gains[0] = 0.0f;
1667
+ gains[1] = 1.0f;
1668
+ } else if ((radians >= -RADIANS_135_DEGREES) && (radians <= -RADIANS_45_DEGREES)) {
1669
+ gains[0] = 1.0f;
1670
+ gains[1] = 0.0f;
1671
+ } else if (radians < 0.0f) { /* back left */
1672
+ ALfloat sine, cosine;
1673
+ calculate_sincos((ALfloat) -(radians + M_PI), &sine, &cosine);
1674
+ gains[0] = (SQRT2_DIV2 * (cosine - sine));
1675
+ gains[1] = (SQRT2_DIV2 * (cosine + sine));
1676
+ } else { /* back right */
1677
+ ALfloat sine, cosine;
1678
+ calculate_sincos((ALfloat) -(radians - M_PI), &sine, &cosine);
1679
+ gains[0] = (SQRT2_DIV2 * (cosine - sine));
1680
+ gains[1] = (SQRT2_DIV2 * (cosine + sine));
1681
+ }
1682
+
1683
+ /* apply distance attenuation and gain to positioning. */
1684
+ gains[0] *= gain;
1685
+ gains[1] *= gain;
1686
+ }
1687
+
1688
+
1689
+ static ALCboolean mix_source(ALCcontext *ctx, ALsource *src, float *stream, int len, const ALboolean force_recalc)
1690
+ {
1691
+ ALCboolean keep;
1692
+
1693
+ keep = (SDL_AtomicGet(&src->state) == AL_PLAYING);
1694
+ if (keep) {
1695
+ SDL_assert(src->allocated);
1696
+ if (src->recalc || force_recalc) {
1697
+ SDL_MemoryBarrierAcquire();
1698
+ src->recalc = AL_FALSE;
1699
+ calculate_channel_gains(ctx, src, src->panning);
1700
+ }
1701
+ if (src->type == AL_STATIC) {
1702
+ BufferQueueItem fakequeue = { src->buffer, NULL };
1703
+ keep = mix_source_buffer_queue(ctx, src, &fakequeue, stream, len);
1704
+ } else if (src->type == AL_STREAMING) {
1705
+ obtain_newly_queued_buffers(&src->buffer_queue);
1706
+ keep = mix_source_buffer_queue(ctx, src, src->buffer_queue.head, stream, len);
1707
+ } else if (src->type == AL_UNDETERMINED) {
1708
+ keep = ALC_FALSE; /* this has AL_BUFFER set to 0; just dump it. */
1709
+ } else {
1710
+ SDL_assert(!"unknown source type");
1711
+ }
1712
+ }
1713
+
1714
+ return keep;
1715
+ }
1716
+
1717
+ /* move new play requests over to the mixer thread. */
1718
+ static void migrate_playlist_requests(ALCcontext *ctx)
1719
+ {
1720
+ SourcePlayTodo *todo;
1721
+ SourcePlayTodo *todoend;
1722
+ SourcePlayTodo *i;
1723
+
1724
+ do { /* take the todo list atomically, now we own it. */
1725
+ todo = (SourcePlayTodo *) ctx->playlist_todo;
1726
+ } while (!SDL_AtomicCASPtr(&ctx->playlist_todo, todo, NULL));
1727
+
1728
+ if (!todo) {
1729
+ return; /* nothing new. */
1730
+ }
1731
+
1732
+ todoend = todo;
1733
+
1734
+ /* ctx->playlist and ALsource->playlist_next are only every touched
1735
+ by the mixer thread, and source pointers live until context destruction. */
1736
+ for (i = todo; i != NULL; i = i->next) {
1737
+ todoend = i;
1738
+ if ((i->source != ctx->playlist_tail) && (!i->source->playlist_next)) {
1739
+ i->source->playlist_next = ctx->playlist;
1740
+ if (!ctx->playlist) {
1741
+ ctx->playlist_tail = i->source;
1742
+ }
1743
+ ctx->playlist = i->source;
1744
+ }
1745
+ }
1746
+
1747
+ /* put these objects back in the pool for reuse */
1748
+ do {
1749
+ todoend->next = i = (SourcePlayTodo *) ctx->device->playback.source_todo_pool;
1750
+ } while (!SDL_AtomicCASPtr(&ctx->device->playback.source_todo_pool, i, todo));
1751
+ }
1752
+
1753
+ static void mix_context(ALCcontext *ctx, float *stream, int len)
1754
+ {
1755
+ const ALboolean force_recalc = ctx->recalc;
1756
+ ALsource *next = NULL;
1757
+ ALsource *prev = NULL;
1758
+ ALsource *i;
1759
+
1760
+ if (force_recalc) {
1761
+ SDL_MemoryBarrierAcquire();
1762
+ ctx->recalc = AL_FALSE;
1763
+ }
1764
+
1765
+ migrate_playlist_requests(ctx);
1766
+
1767
+ for (i = ctx->playlist; i != NULL; i = next) {
1768
+ next = i->playlist_next; /* save this to a local in case we leave the list. */
1769
+
1770
+ SDL_LockMutex(ctx->source_lock);
1771
+ if (!mix_source(ctx, i, stream, len, force_recalc)) {
1772
+ /* take it out of the playlist. It wasn't actually playing or it just finished. */
1773
+ i->playlist_next = NULL;
1774
+ if (next == NULL) {
1775
+ SDL_assert(i == ctx->playlist_tail);
1776
+ ctx->playlist_tail = prev;
1777
+ }
1778
+ if (prev) {
1779
+ prev->playlist_next = next;
1780
+ } else {
1781
+ SDL_assert(i == ctx->playlist);
1782
+ ctx->playlist = next;
1783
+ }
1784
+ SDL_AtomicSet(&i->mixer_accessible, 0);
1785
+ } else {
1786
+ prev = i;
1787
+ }
1788
+ SDL_UnlockMutex(ctx->source_lock);
1789
+ }
1790
+ }
1791
+
1792
+ /* Disconnected devices move all PLAYING sources to STOPPED, making their buffer queues processed. */
1793
+ static void mix_disconnected_context(ALCcontext *ctx)
1794
+ {
1795
+ ALsource *next = NULL;
1796
+ ALsource *i;
1797
+
1798
+ migrate_playlist_requests(ctx);
1799
+
1800
+ for (i = ctx->playlist; i != NULL; i = next) {
1801
+ next = i->playlist_next;
1802
+
1803
+ SDL_LockMutex(ctx->source_lock);
1804
+ /* remove from playlist; all playing things got stopped, paused/initial/stopped shouldn't be listed. */
1805
+ if (SDL_AtomicGet(&i->state) == AL_PLAYING) {
1806
+ SDL_assert(i->allocated);
1807
+ SDL_AtomicSet(&i->state, AL_STOPPED);
1808
+ source_mark_all_buffers_processed(i);
1809
+ }
1810
+
1811
+ i->playlist_next = NULL;
1812
+ SDL_AtomicSet(&i->mixer_accessible, 0);
1813
+ SDL_UnlockMutex(ctx->source_lock);
1814
+ }
1815
+ ctx->playlist = NULL;
1816
+ ctx->playlist_tail = NULL;
1817
+ }
1818
+
1819
+ /* We process all unsuspended ALC contexts during this call, mixing their
1820
+ output to (stream). SDL then plays this mixed audio to the hardware. */
1821
+ static void SDLCALL playback_device_callback(void *userdata, Uint8 *stream, int len)
1822
+ {
1823
+ ALCdevice *device = (ALCdevice *) userdata;
1824
+ ALCcontext *ctx;
1825
+ ALCboolean connected = ALC_FALSE;
1826
+
1827
+ SDL_memset(stream, '\0', len);
1828
+
1829
+ if (SDL_AtomicGet(&device->connected)) {
1830
+ if (SDL_GetAudioDeviceStatus(device->sdldevice) == SDL_AUDIO_STOPPED) {
1831
+ SDL_AtomicSet(&device->connected, ALC_FALSE);
1832
+ } else {
1833
+ connected = ALC_TRUE;
1834
+ }
1835
+ }
1836
+
1837
+ for (ctx = device->playback.contexts; ctx != NULL; ctx = ctx->next) {
1838
+ if (SDL_AtomicGet(&ctx->processing)) {
1839
+ if (connected) {
1840
+ mix_context(ctx, (float *) stream, len);
1841
+ } else {
1842
+ mix_disconnected_context(ctx);
1843
+ }
1844
+ }
1845
+ }
1846
+ }
1847
+
1848
+ static ALCcontext *_alcCreateContext(ALCdevice *device, const ALCint* attrlist)
1849
+ {
1850
+ ALCcontext *retval = NULL;
1851
+ ALCsizei attrcount = 0;
1852
+ ALCint freq = 48000;
1853
+ ALCboolean sync = ALC_FALSE;
1854
+ ALCint refresh = 100;
1855
+ /* we don't care about ALC_MONO_SOURCES or ALC_STEREO_SOURCES as we have no hardware limitation. */
1856
+
1857
+ if (!device) {
1858
+ set_alc_error(NULL, ALC_INVALID_DEVICE);
1859
+ return NULL;
1860
+ }
1861
+
1862
+ if (!SDL_AtomicGet(&device->connected)) {
1863
+ set_alc_error(device, ALC_INVALID_DEVICE);
1864
+ return NULL;
1865
+ }
1866
+
1867
+ if (attrlist != NULL) {
1868
+ ALCint attr;
1869
+ while ((attr = attrlist[attrcount++]) != 0) {
1870
+ switch (attr) {
1871
+ case ALC_FREQUENCY: freq = attrlist[attrcount++]; break;
1872
+ case ALC_REFRESH: refresh = attrlist[attrcount++]; break;
1873
+ case ALC_SYNC: sync = (attrlist[attrcount++] ? ALC_TRUE : ALC_FALSE); break;
1874
+ default: FIXME("fail for unknown attributes?"); break;
1875
+ }
1876
+ }
1877
+ }
1878
+
1879
+ FIXME("use these variables at some point"); (void) refresh; (void) sync;
1880
+
1881
+ retval = (ALCcontext *) calloc_simd_aligned(sizeof (ALCcontext));
1882
+ if (!retval) {
1883
+ set_alc_error(device, ALC_OUT_OF_MEMORY);
1884
+ return NULL;
1885
+ }
1886
+
1887
+ /* Make sure everything that wants to use SIMD is aligned for it. */
1888
+ SDL_assert( (((size_t) &retval->listener.position[0]) % 16) == 0 );
1889
+ SDL_assert( (((size_t) &retval->listener.orientation[0]) % 16) == 0 );
1890
+ SDL_assert( (((size_t) &retval->listener.velocity[0]) % 16) == 0 );
1891
+
1892
+ retval->source_lock = SDL_CreateMutex();
1893
+ if (!retval->source_lock) {
1894
+ set_alc_error(device, ALC_OUT_OF_MEMORY);
1895
+ free_simd_aligned(retval);
1896
+ return NULL;
1897
+ }
1898
+
1899
+ retval->attributes = (ALCint *) SDL_malloc(attrcount * sizeof (ALCint));
1900
+ if (!retval->attributes) {
1901
+ set_alc_error(device, ALC_OUT_OF_MEMORY);
1902
+ SDL_DestroyMutex(retval->source_lock);
1903
+ free_simd_aligned(retval);
1904
+ return NULL;
1905
+ }
1906
+ SDL_memcpy(retval->attributes, attrlist, attrcount * sizeof (ALCint));
1907
+ retval->attributes_count = attrcount;
1908
+
1909
+ if (!device->sdldevice) {
1910
+ SDL_AudioSpec desired;
1911
+ const char *devicename = device->name;
1912
+
1913
+ if (SDL_strcmp(devicename, DEFAULT_PLAYBACK_DEVICE) == 0) {
1914
+ devicename = NULL; /* tell SDL we want the best default */
1915
+ }
1916
+
1917
+ /* we always want to work in float32, to keep our work simple and
1918
+ let us use SIMD, and we'll let SDL convert when feeding the device. */
1919
+ SDL_zero(desired);
1920
+ desired.freq = freq;
1921
+ desired.format = AUDIO_F32SYS;
1922
+ desired.channels = 2; FIXME("don't force channels?");
1923
+ desired.samples = 1024; FIXME("base this on refresh");
1924
+ desired.callback = playback_device_callback;
1925
+ desired.userdata = device;
1926
+ device->sdldevice = SDL_OpenAudioDevice(devicename, 0, &desired, NULL, 0);
1927
+ if (!device->sdldevice) {
1928
+ SDL_DestroyMutex(retval->source_lock);
1929
+ SDL_free(retval->attributes);
1930
+ free_simd_aligned(retval);
1931
+ FIXME("What error do you set for this?");
1932
+ return NULL;
1933
+ }
1934
+ device->channels = 2;
1935
+ device->frequency = freq;
1936
+ device->framesize = sizeof (float) * device->channels;
1937
+ SDL_PauseAudioDevice(device->sdldevice, 0);
1938
+ }
1939
+
1940
+ retval->distance_model = AL_INVERSE_DISTANCE_CLAMPED;
1941
+ retval->doppler_factor = 1.0f;
1942
+ retval->doppler_velocity = 1.0f;
1943
+ retval->speed_of_sound = 343.3f;
1944
+ retval->listener.gain = 1.0f;
1945
+ retval->listener.orientation[2] = -1.0f;
1946
+ retval->listener.orientation[5] = 1.0f;
1947
+ retval->device = device;
1948
+ context_needs_recalc(retval);
1949
+ SDL_AtomicSet(&retval->processing, 1); /* contexts default to processing */
1950
+
1951
+ SDL_LockAudioDevice(device->sdldevice);
1952
+ if (device->playback.contexts != NULL) {
1953
+ SDL_assert(device->playback.contexts->prev == NULL);
1954
+ device->playback.contexts->prev = retval;
1955
+ }
1956
+ retval->next = device->playback.contexts;
1957
+ device->playback.contexts = retval;
1958
+ SDL_UnlockAudioDevice(device->sdldevice);
1959
+
1960
+ return retval;
1961
+ }
1962
+ ENTRYPOINT(ALCcontext *,alcCreateContext,(ALCdevice *device, const ALCint* attrlist),(device,attrlist))
1963
+
1964
+
1965
+ static SDL_INLINE ALCcontext *get_current_context(void)
1966
+ {
1967
+ return (ALCcontext *) SDL_AtomicGetPtr(&current_context);
1968
+ }
1969
+
1970
+ /* no api lock; it just sets an atomic pointer at the moment */
1971
+ ALCboolean alcMakeContextCurrent(ALCcontext *ctx)
1972
+ {
1973
+ SDL_AtomicSetPtr(&current_context, ctx);
1974
+ FIXME("any reason this might return ALC_FALSE?");
1975
+ return ALC_TRUE;
1976
+ }
1977
+
1978
+ static void _alcProcessContext(ALCcontext *ctx)
1979
+ {
1980
+ if (!ctx) {
1981
+ set_alc_error(NULL, ALC_INVALID_CONTEXT);
1982
+ return;
1983
+ }
1984
+
1985
+ SDL_assert(!ctx->device->iscapture);
1986
+ SDL_AtomicSet(&ctx->processing, 1);
1987
+ }
1988
+ ENTRYPOINTVOID(alcProcessContext,(ALCcontext *ctx),(ctx))
1989
+
1990
+ static void _alcSuspendContext(ALCcontext *ctx)
1991
+ {
1992
+ if (!ctx) {
1993
+ set_alc_error(NULL, ALC_INVALID_CONTEXT);
1994
+ } else {
1995
+ SDL_assert(!ctx->device->iscapture);
1996
+ SDL_AtomicSet(&ctx->processing, 0);
1997
+ }
1998
+ }
1999
+ ENTRYPOINTVOID(alcSuspendContext,(ALCcontext *ctx),(ctx))
2000
+
2001
+ static void _alcDestroyContext(ALCcontext *ctx)
2002
+ {
2003
+ ALsizei blocki;
2004
+
2005
+ FIXME("Should NULL context be an error?");
2006
+ if (!ctx) return;
2007
+
2008
+ /* The spec says it's illegal to delete the current context. */
2009
+ if (get_current_context() == ctx) {
2010
+ set_alc_error(ctx->device, ALC_INVALID_CONTEXT);
2011
+ return;
2012
+ }
2013
+
2014
+ /* do this first in case the mixer is running _right now_. */
2015
+ SDL_AtomicSet(&ctx->processing, 0);
2016
+
2017
+ SDL_LockAudioDevice(ctx->device->sdldevice);
2018
+ if (ctx->prev) {
2019
+ ctx->prev->next = ctx->next;
2020
+ } else {
2021
+ SDL_assert(ctx == ctx->device->playback.contexts);
2022
+ ctx->device->playback.contexts = ctx->next;
2023
+ }
2024
+ if (ctx->next) {
2025
+ ctx->next->prev = ctx->prev;
2026
+ }
2027
+ SDL_UnlockAudioDevice(ctx->device->sdldevice);
2028
+
2029
+ for (blocki = 0; blocki < ctx->num_source_blocks; blocki++) {
2030
+ SourceBlock *sb = ctx->source_blocks[blocki];
2031
+ if (sb->used > 0) {
2032
+ ALsizei i;
2033
+ for (i = 0; i < SDL_arraysize(sb->sources); i++) {
2034
+ ALsource *src = &sb->sources[i];
2035
+ if (!src->allocated) {
2036
+ continue;
2037
+ }
2038
+
2039
+ SDL_FreeAudioStream(src->stream);
2040
+ source_release_buffer_queue(ctx, src);
2041
+ if (--sb->used == 0) {
2042
+ break;
2043
+ }
2044
+ }
2045
+ }
2046
+ free_simd_aligned(sb);
2047
+ }
2048
+
2049
+ SDL_DestroyMutex(ctx->source_lock);
2050
+ SDL_free(ctx->source_blocks);
2051
+ SDL_free(ctx->attributes);
2052
+ free_simd_aligned(ctx);
2053
+ }
2054
+ ENTRYPOINTVOID(alcDestroyContext,(ALCcontext *ctx),(ctx))
2055
+
2056
+ /* no api lock; atomic. */
2057
+ ALCcontext *alcGetCurrentContext(void)
2058
+ {
2059
+ return get_current_context();
2060
+ }
2061
+
2062
+ /* no api lock; immutable. */
2063
+ ALCdevice *alcGetContextsDevice(ALCcontext *context)
2064
+ {
2065
+ return context ? context->device : NULL;
2066
+ }
2067
+
2068
+ static ALCenum _alcGetError(ALCdevice *device)
2069
+ {
2070
+ ALCenum *perr = device ? &device->error : &null_device_error;
2071
+ const ALCenum retval = *perr;
2072
+ *perr = ALC_NO_ERROR;
2073
+ return retval;
2074
+ }
2075
+ ENTRYPOINT(ALCenum,alcGetError,(ALCdevice *device),(device))
2076
+
2077
+ /* no api lock; immutable */
2078
+ ALCboolean alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname)
2079
+ {
2080
+ #define ALC_EXTENSION_ITEM(ext) if (SDL_strcasecmp(extname, #ext) == 0) { return ALC_TRUE; }
2081
+ ALC_EXTENSION_ITEMS
2082
+ #undef ALC_EXTENSION_ITEM
2083
+ return ALC_FALSE;
2084
+ }
2085
+
2086
+ /* no api lock; immutable */
2087
+ void *alcGetProcAddress(ALCdevice *device, const ALCchar *funcname)
2088
+ {
2089
+ if (!funcname) {
2090
+ set_alc_error(device, ALC_INVALID_VALUE);
2091
+ return NULL;
2092
+ }
2093
+
2094
+ #define FN_TEST(fn) if (SDL_strcmp(funcname, #fn) == 0) return (void *) fn
2095
+ FN_TEST(alcCreateContext);
2096
+ FN_TEST(alcMakeContextCurrent);
2097
+ FN_TEST(alcProcessContext);
2098
+ FN_TEST(alcSuspendContext);
2099
+ FN_TEST(alcDestroyContext);
2100
+ FN_TEST(alcGetCurrentContext);
2101
+ FN_TEST(alcGetContextsDevice);
2102
+ FN_TEST(alcOpenDevice);
2103
+ FN_TEST(alcCloseDevice);
2104
+ FN_TEST(alcGetError);
2105
+ FN_TEST(alcIsExtensionPresent);
2106
+ FN_TEST(alcGetProcAddress);
2107
+ FN_TEST(alcGetEnumValue);
2108
+ FN_TEST(alcGetString);
2109
+ FN_TEST(alcGetIntegerv);
2110
+ FN_TEST(alcCaptureOpenDevice);
2111
+ FN_TEST(alcCaptureCloseDevice);
2112
+ FN_TEST(alcCaptureStart);
2113
+ FN_TEST(alcCaptureStop);
2114
+ FN_TEST(alcCaptureSamples);
2115
+ #undef FN_TEST
2116
+
2117
+ set_alc_error(device, ALC_INVALID_VALUE);
2118
+ return NULL;
2119
+ }
2120
+
2121
+ /* no api lock; immutable */
2122
+ ALCenum alcGetEnumValue(ALCdevice *device, const ALCchar *enumname)
2123
+ {
2124
+ if (!enumname) {
2125
+ set_alc_error(device, ALC_INVALID_VALUE);
2126
+ return (ALCenum) AL_NONE;
2127
+ }
2128
+
2129
+ #define ENUM_TEST(en) if (SDL_strcmp(enumname, #en) == 0) return en
2130
+ ENUM_TEST(ALC_FALSE);
2131
+ ENUM_TEST(ALC_TRUE);
2132
+ ENUM_TEST(ALC_FREQUENCY);
2133
+ ENUM_TEST(ALC_REFRESH);
2134
+ ENUM_TEST(ALC_SYNC);
2135
+ ENUM_TEST(ALC_MONO_SOURCES);
2136
+ ENUM_TEST(ALC_STEREO_SOURCES);
2137
+ ENUM_TEST(ALC_NO_ERROR);
2138
+ ENUM_TEST(ALC_INVALID_DEVICE);
2139
+ ENUM_TEST(ALC_INVALID_CONTEXT);
2140
+ ENUM_TEST(ALC_INVALID_ENUM);
2141
+ ENUM_TEST(ALC_INVALID_VALUE);
2142
+ ENUM_TEST(ALC_OUT_OF_MEMORY);
2143
+ ENUM_TEST(ALC_MAJOR_VERSION);
2144
+ ENUM_TEST(ALC_MINOR_VERSION);
2145
+ ENUM_TEST(ALC_ATTRIBUTES_SIZE);
2146
+ ENUM_TEST(ALC_ALL_ATTRIBUTES);
2147
+ ENUM_TEST(ALC_DEFAULT_DEVICE_SPECIFIER);
2148
+ ENUM_TEST(ALC_DEVICE_SPECIFIER);
2149
+ ENUM_TEST(ALC_EXTENSIONS);
2150
+ ENUM_TEST(ALC_CAPTURE_DEVICE_SPECIFIER);
2151
+ ENUM_TEST(ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
2152
+ ENUM_TEST(ALC_CAPTURE_SAMPLES);
2153
+ ENUM_TEST(ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
2154
+ ENUM_TEST(ALC_ALL_DEVICES_SPECIFIER);
2155
+ ENUM_TEST(ALC_CONNECTED);
2156
+ #undef ENUM_TEST
2157
+
2158
+ set_alc_error(device, ALC_INVALID_VALUE);
2159
+ return (ALCenum) AL_NONE;
2160
+ }
2161
+
2162
+ static const ALCchar *calculate_sdl_device_list(const int iscapture)
2163
+ {
2164
+ /* alcGetString() has to return a const string that is not freed and might
2165
+ continue to live even if we update this list in a later query, so we
2166
+ just make a big static buffer and hope it's large enough and that other
2167
+ race conditions don't bite us. The enumeration extension shouldn't have
2168
+ reused entry points, or done this silly null-delimited string list.
2169
+ Oh well. */
2170
+ #define DEVICE_LIST_BUFFER_SIZE 512
2171
+ static ALCchar playback_list[DEVICE_LIST_BUFFER_SIZE];
2172
+ static ALCchar capture_list[DEVICE_LIST_BUFFER_SIZE];
2173
+ ALCchar *final_list = iscapture ? capture_list : playback_list;
2174
+ ALCchar *ptr = final_list;
2175
+ int numdevs;
2176
+ size_t avail = DEVICE_LIST_BUFFER_SIZE;
2177
+ size_t cpy;
2178
+ int i;
2179
+
2180
+ /* default device is always available. */
2181
+ cpy = SDL_strlcpy(ptr, iscapture ? DEFAULT_CAPTURE_DEVICE : DEFAULT_PLAYBACK_DEVICE, avail);
2182
+ SDL_assert((cpy+1) < avail);
2183
+ ptr += cpy + 1; /* skip past null char. */
2184
+ avail -= cpy + 1;
2185
+
2186
+ if (SDL_InitSubSystem(SDL_INIT_AUDIO) == -1) {
2187
+ return NULL;
2188
+ }
2189
+
2190
+ if (!init_api_lock()) {
2191
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
2192
+ return NULL;
2193
+ }
2194
+
2195
+ grab_api_lock();
2196
+
2197
+ numdevs = SDL_GetNumAudioDevices(iscapture);
2198
+
2199
+ for (i = 0; i < numdevs; i++) {
2200
+ const char *devname = SDL_GetAudioDeviceName(i, iscapture);
2201
+ const size_t devnamelen = SDL_strlen(devname);
2202
+ /* if we're out of space, we just have to drop devices we can't cram in the buffer. */
2203
+ if (avail > (devnamelen + 2)) {
2204
+ cpy = SDL_strlcpy(ptr, devname, avail);
2205
+ SDL_assert(cpy == devnamelen);
2206
+ SDL_assert((cpy+1) < avail);
2207
+ ptr += cpy + 1; /* skip past null char. */
2208
+ avail -= cpy + 1;
2209
+ }
2210
+ }
2211
+
2212
+ SDL_assert(avail >= 1);
2213
+ *ptr = '\0';
2214
+
2215
+ ungrab_api_lock();
2216
+
2217
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
2218
+
2219
+ return final_list;
2220
+
2221
+ #undef DEVICE_LIST_BUFFER_SIZE
2222
+ }
2223
+
2224
+ /* no api lock; immutable (unless it isn't, then we manually lock). */
2225
+ const ALCchar *alcGetString(ALCdevice *device, ALCenum param)
2226
+ {
2227
+ switch (param) {
2228
+ case ALC_EXTENSIONS: {
2229
+ #define ALC_EXTENSION_ITEM(ext) " " #ext
2230
+ static ALCchar alc_extensions_string[] = ALC_EXTENSION_ITEMS;
2231
+ #undef ALC_EXTENSION_ITEM
2232
+ return alc_extensions_string + 1; /* skip that first space char */
2233
+ }
2234
+
2235
+ /* You open the default SDL device with a NULL device name, but that is how OpenAL
2236
+ reports an error here, so we give it a magic identifier here instead. */
2237
+ case ALC_DEFAULT_DEVICE_SPECIFIER:
2238
+ return DEFAULT_PLAYBACK_DEVICE;
2239
+
2240
+ case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER:
2241
+ return DEFAULT_CAPTURE_DEVICE;
2242
+
2243
+ case ALC_DEVICE_SPECIFIER:
2244
+ FIXME("should return NULL if device->iscapture?");
2245
+ return device ? device->name : calculate_sdl_device_list(0);
2246
+
2247
+ case ALC_CAPTURE_DEVICE_SPECIFIER:
2248
+ FIXME("should return NULL if !device->iscapture?");
2249
+ return device ? device->name : calculate_sdl_device_list(1);
2250
+
2251
+ case ALC_NO_ERROR: return "ALC_NO_ERROR";
2252
+ case ALC_INVALID_DEVICE: return "ALC_INVALID_DEVICE";
2253
+ case ALC_INVALID_CONTEXT:return "ALC_INVALID_CONTEXT";
2254
+ case ALC_INVALID_ENUM: return "ALC_INVALID_ENUM";
2255
+ case ALC_INVALID_VALUE: return "ALC_INVALID_VALUE";
2256
+ case ALC_OUT_OF_MEMORY: return "ALC_OUT_OF_MEMORY";
2257
+
2258
+ default: break;
2259
+ }
2260
+
2261
+ FIXME("other enums that should report as strings?");
2262
+ set_alc_error(device, ALC_INVALID_ENUM);
2263
+ return NULL;
2264
+ }
2265
+
2266
+ static void _alcGetIntegerv(ALCdevice *device, const ALCenum param, const ALCsizei size, ALCint *values)
2267
+ {
2268
+ ALCcontext *ctx = NULL;
2269
+
2270
+ if (!size || !values) {
2271
+ return; /* "A NULL destination or a zero size parameter will cause ALC to ignore the query." */
2272
+ }
2273
+
2274
+ switch (param) {
2275
+ case ALC_CAPTURE_SAMPLES:
2276
+ if (!device || !device->iscapture) {
2277
+ set_alc_error(device, ALC_INVALID_DEVICE);
2278
+ return;
2279
+ }
2280
+
2281
+ FIXME("make ring buffer atomic?");
2282
+ SDL_LockAudioDevice(device->sdldevice);
2283
+ *values = (ALCint) (device->capture.ring.used / device->framesize);
2284
+ SDL_UnlockAudioDevice(device->sdldevice);
2285
+ return;
2286
+
2287
+ case ALC_CONNECTED:
2288
+ if (device) {
2289
+ *values = SDL_AtomicGet(&device->connected) ? ALC_TRUE : ALC_FALSE;
2290
+ } else {
2291
+ *values = ALC_FALSE;
2292
+ set_alc_error(device, ALC_INVALID_DEVICE);
2293
+ }
2294
+ return;
2295
+
2296
+ case ALC_ATTRIBUTES_SIZE:
2297
+ case ALC_ALL_ATTRIBUTES:
2298
+ if (!device || device->iscapture) {
2299
+ *values = 0;
2300
+ set_alc_error(device, ALC_INVALID_DEVICE);
2301
+ return;
2302
+ }
2303
+
2304
+ ctx = get_current_context();
2305
+
2306
+ FIXME("wants 'current context of specified device', but there isn't a current context per-device...");
2307
+ if ((!ctx) || (ctx->device != device)) {
2308
+ *values = 0;
2309
+ set_alc_error(device, ALC_INVALID_CONTEXT);
2310
+ return;
2311
+ }
2312
+
2313
+ if (param == ALC_ALL_ATTRIBUTES) {
2314
+ if (size < ctx->attributes_count) {
2315
+ *values = 0;
2316
+ set_alc_error(device, ALC_INVALID_VALUE);
2317
+ return;
2318
+ }
2319
+ SDL_memcpy(values, ctx->attributes, ctx->attributes_count * sizeof (ALCint));
2320
+ } else {
2321
+ *values = (ALCint) ctx->attributes_count;
2322
+ }
2323
+ return;
2324
+
2325
+ case ALC_MAJOR_VERSION:
2326
+ *values = OPENAL_VERSION_MAJOR;
2327
+ return;
2328
+
2329
+ case ALC_MINOR_VERSION:
2330
+ *values = OPENAL_VERSION_MINOR;
2331
+ return;
2332
+
2333
+ default: break;
2334
+ }
2335
+
2336
+ set_alc_error(device, ALC_INVALID_ENUM);
2337
+ *values = 0;
2338
+ }
2339
+ ENTRYPOINTVOID(alcGetIntegerv,(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values),(device,param,size,values))
2340
+
2341
+
2342
+ /* audio callback for capture devices just needs to move data into our
2343
+ ringbuffer for later recovery by the app in alcCaptureSamples(). SDL
2344
+ should have handled resampling and conversion for us to the expected
2345
+ audio format. */
2346
+ static void SDLCALL capture_device_callback(void *userdata, Uint8 *stream, int len)
2347
+ {
2348
+ ALCdevice *device = (ALCdevice *) userdata;
2349
+ ALCboolean connected = ALC_FALSE;
2350
+ SDL_assert(device->iscapture);
2351
+
2352
+ if (SDL_AtomicGet(&device->connected)) {
2353
+ if (SDL_GetAudioDeviceStatus(device->sdldevice) == SDL_AUDIO_STOPPED) {
2354
+ SDL_AtomicSet(&device->connected, ALC_FALSE);
2355
+ } else {
2356
+ connected = ALC_TRUE;
2357
+ }
2358
+ }
2359
+
2360
+ if (connected) {
2361
+ ring_buffer_put(&device->capture.ring, stream, (ALCsizei) len);
2362
+ }
2363
+ }
2364
+
2365
+ /* no api lock; this creates it and otherwise doesn't have any state that can race */
2366
+ ALCdevice *alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize)
2367
+ {
2368
+ SDL_AudioSpec desired;
2369
+ ALCsizei framesize = 0;
2370
+ const char *sdldevname = NULL;
2371
+ ALCdevice *device = NULL;
2372
+ ALCubyte *ringbuf = NULL;
2373
+
2374
+ SDL_zero(desired);
2375
+ if (!alcfmt_to_sdlfmt(format, &desired.format, &desired.channels, &framesize)) {
2376
+ return NULL;
2377
+ }
2378
+
2379
+ if (!devicename) {
2380
+ devicename = DEFAULT_CAPTURE_DEVICE; /* so ALC_CAPTURE_DEVICE_SPECIFIER is meaningful */
2381
+ }
2382
+
2383
+ desired.freq = frequency;
2384
+ desired.samples = 1024; FIXME("is this a reasonable value?");
2385
+ desired.callback = capture_device_callback;
2386
+
2387
+ if (SDL_strcmp(devicename, DEFAULT_CAPTURE_DEVICE) != 0) {
2388
+ sdldevname = devicename; /* we want NULL for the best SDL default unless app is explicit. */
2389
+ }
2390
+
2391
+ device = prep_alc_device(devicename, ALC_TRUE);
2392
+ if (!device) {
2393
+ return NULL;
2394
+ }
2395
+
2396
+ device->frequency = frequency;
2397
+ device->framesize = framesize;
2398
+ device->capture.ring.size = framesize * buffersize;
2399
+
2400
+ if (device->capture.ring.size >= buffersize) {
2401
+ ringbuf = (ALCubyte *) SDL_malloc(device->capture.ring.size);
2402
+ }
2403
+
2404
+ if (!ringbuf) {
2405
+ SDL_free(device->name);
2406
+ SDL_free(device);
2407
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
2408
+ return NULL;
2409
+ }
2410
+
2411
+ device->capture.ring.buffer = ringbuf;
2412
+
2413
+ desired.userdata = device;
2414
+
2415
+ device->sdldevice = SDL_OpenAudioDevice(sdldevname, 1, &desired, NULL, 0);
2416
+ if (!device->sdldevice) {
2417
+ SDL_free(ringbuf);
2418
+ SDL_free(device->name);
2419
+ SDL_free(device);
2420
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
2421
+ return NULL;
2422
+ }
2423
+
2424
+ return device;
2425
+ }
2426
+
2427
+ /* no api lock; this requires you to not destroy a device that's still in use */
2428
+ ALCboolean alcCaptureCloseDevice(ALCdevice *device)
2429
+ {
2430
+ if (!device || !device->iscapture) {
2431
+ return ALC_FALSE;
2432
+ }
2433
+
2434
+ if (device->sdldevice) {
2435
+ SDL_CloseAudioDevice(device->sdldevice);
2436
+ }
2437
+
2438
+ SDL_free(device->capture.ring.buffer);
2439
+ SDL_free(device->name);
2440
+ SDL_free(device);
2441
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
2442
+
2443
+ return ALC_TRUE;
2444
+ }
2445
+
2446
+ static void _alcCaptureStart(ALCdevice *device)
2447
+ {
2448
+ if (device && device->iscapture) {
2449
+ /* alcCaptureStart() drops any previously-buffered data. */
2450
+ FIXME("does this clear the ring buffer if the device is already started?");
2451
+ SDL_LockAudioDevice(device->sdldevice);
2452
+ device->capture.ring.read = 0;
2453
+ device->capture.ring.write = 0;
2454
+ device->capture.ring.used = 0;
2455
+ SDL_UnlockAudioDevice(device->sdldevice);
2456
+ SDL_PauseAudioDevice(device->sdldevice, 0);
2457
+ }
2458
+ }
2459
+ ENTRYPOINTVOID(alcCaptureStart,(ALCdevice *device),(device))
2460
+
2461
+ static void _alcCaptureStop(ALCdevice *device)
2462
+ {
2463
+ if (device && device->iscapture) {
2464
+ SDL_PauseAudioDevice(device->sdldevice, 1);
2465
+ }
2466
+ }
2467
+ ENTRYPOINTVOID(alcCaptureStop,(ALCdevice *device),(device))
2468
+
2469
+ static void _alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, const ALCsizei samples)
2470
+ {
2471
+ ALCsizei requested_bytes;
2472
+ if (!device || !device->iscapture) {
2473
+ return;
2474
+ }
2475
+
2476
+ requested_bytes = samples * device->framesize;
2477
+
2478
+ SDL_LockAudioDevice(device->sdldevice);
2479
+ if (requested_bytes > device->capture.ring.used) {
2480
+ SDL_UnlockAudioDevice(device->sdldevice);
2481
+ FIXME("set error state?");
2482
+ return; /* this is an error state, according to the spec. */
2483
+ }
2484
+
2485
+ ring_buffer_get(&device->capture.ring, buffer, requested_bytes);
2486
+ SDL_UnlockAudioDevice(device->sdldevice);
2487
+ }
2488
+ ENTRYPOINTVOID(alcCaptureSamples,(ALCdevice *device, ALCvoid *buffer, ALCsizei samples),(device,buffer,samples))
2489
+
2490
+
2491
+ /* AL implementation... */
2492
+
2493
+ static ALenum null_context_error = AL_NO_ERROR;
2494
+
2495
+ static void set_al_error(ALCcontext *ctx, const ALenum error)
2496
+ {
2497
+ ALenum *perr = ctx ? &ctx->error : &null_context_error;
2498
+ /* can't set a new error when the previous hasn't been cleared yet. */
2499
+ if (*perr == AL_NO_ERROR) {
2500
+ *perr = error;
2501
+ }
2502
+ }
2503
+
2504
+ /* !!! FIXME: buffers and sources use almost identical code for blocks */
2505
+ static ALsource *get_source(ALCcontext *ctx, const ALuint name, SourceBlock **_block)
2506
+ {
2507
+ const ALsizei blockidx = (((ALsizei) name) - 1) / OPENAL_SOURCE_BLOCK_SIZE;
2508
+ const ALsizei block_offset = (((ALsizei) name) - 1) % OPENAL_SOURCE_BLOCK_SIZE;
2509
+ ALsource *source;
2510
+ SourceBlock *block;
2511
+
2512
+ /*printf("get_source(%d): blockidx=%d, block_offset=%d\n", (int) name, (int) blockidx, (int) block_offset);*/
2513
+
2514
+ if (!ctx) {
2515
+ set_al_error(ctx, AL_INVALID_OPERATION);
2516
+ if (_block) *_block = NULL;
2517
+ return NULL;
2518
+ } else if ((name == 0) || (blockidx >= ctx->num_source_blocks)) {
2519
+ set_al_error(ctx, AL_INVALID_NAME);
2520
+ if (_block) *_block = NULL;
2521
+ return NULL;
2522
+ }
2523
+
2524
+ block = ctx->source_blocks[blockidx];
2525
+ source = &block->sources[block_offset];
2526
+ if (source->allocated) {
2527
+ if (_block) *_block = block;
2528
+ return source;
2529
+ }
2530
+
2531
+ if (_block) *_block = NULL;
2532
+ set_al_error(ctx, AL_INVALID_NAME);
2533
+ return NULL;
2534
+ }
2535
+
2536
+ /* !!! FIXME: buffers and sources use almost identical code for blocks */
2537
+ static ALbuffer *get_buffer(ALCcontext *ctx, const ALuint name, BufferBlock **_block)
2538
+ {
2539
+ const ALsizei blockidx = (((ALsizei) name) - 1) / OPENAL_BUFFER_BLOCK_SIZE;
2540
+ const ALsizei block_offset = (((ALsizei) name) - 1) % OPENAL_BUFFER_BLOCK_SIZE;
2541
+ ALbuffer *buffer;
2542
+ BufferBlock *block;
2543
+
2544
+ /*printf("get_buffer(%d): blockidx=%d, block_offset=%d\n", (int) name, (int) blockidx, (int) block_offset);*/
2545
+
2546
+ if (!ctx) {
2547
+ set_al_error(ctx, AL_INVALID_OPERATION);
2548
+ if (_block) *_block = NULL;
2549
+ return NULL;
2550
+ } else if ((name == 0) || (blockidx >= ctx->device->playback.num_buffer_blocks)) {
2551
+ set_al_error(ctx, AL_INVALID_NAME);
2552
+ if (_block) *_block = NULL;
2553
+ return NULL;
2554
+ }
2555
+
2556
+ block = ctx->device->playback.buffer_blocks[blockidx];
2557
+ buffer = &block->buffers[block_offset];
2558
+ if (buffer->allocated) {
2559
+ if (_block) *_block = block;
2560
+ return buffer;
2561
+ }
2562
+
2563
+ if (_block) *_block = NULL;
2564
+ set_al_error(ctx, AL_INVALID_NAME);
2565
+ return NULL;
2566
+ }
2567
+
2568
+ static void _alDopplerFactor(const ALfloat value)
2569
+ {
2570
+ ALCcontext *ctx = get_current_context();
2571
+ if (!ctx) {
2572
+ set_al_error(ctx, AL_INVALID_OPERATION);
2573
+ } else if (value < 0.0f) {
2574
+ set_al_error(ctx, AL_INVALID_VALUE);
2575
+ } else {
2576
+ ctx->doppler_factor = value;
2577
+ context_needs_recalc(ctx);
2578
+ }
2579
+ }
2580
+ ENTRYPOINTVOID(alDopplerFactor,(ALfloat value),(value))
2581
+
2582
+ static void _alDopplerVelocity(const ALfloat value)
2583
+ {
2584
+ ALCcontext *ctx = get_current_context();
2585
+ if (!ctx) {
2586
+ set_al_error(ctx, AL_INVALID_OPERATION);
2587
+ } else if (value < 0.0f) {
2588
+ set_al_error(ctx, AL_INVALID_VALUE);
2589
+ } else {
2590
+ ctx->doppler_velocity = value;
2591
+ context_needs_recalc(ctx);
2592
+ }
2593
+ }
2594
+ ENTRYPOINTVOID(alDopplerVelocity,(ALfloat value),(value))
2595
+
2596
+ static void _alSpeedOfSound(const ALfloat value)
2597
+ {
2598
+ ALCcontext *ctx = get_current_context();
2599
+ if (!ctx) {
2600
+ set_al_error(ctx, AL_INVALID_OPERATION);
2601
+ } else if (value < 0.0f) {
2602
+ set_al_error(ctx, AL_INVALID_VALUE);
2603
+ } else {
2604
+ ctx->speed_of_sound = value;
2605
+ context_needs_recalc(ctx);
2606
+ }
2607
+ }
2608
+ ENTRYPOINTVOID(alSpeedOfSound,(ALfloat value),(value))
2609
+
2610
+ static void _alDistanceModel(const ALenum model)
2611
+ {
2612
+ ALCcontext *ctx = get_current_context();
2613
+ if (!ctx) {
2614
+ set_al_error(ctx, AL_INVALID_OPERATION);
2615
+ return;
2616
+ }
2617
+
2618
+ switch (model) {
2619
+ case AL_NONE:
2620
+ case AL_INVERSE_DISTANCE:
2621
+ case AL_INVERSE_DISTANCE_CLAMPED:
2622
+ case AL_LINEAR_DISTANCE:
2623
+ case AL_LINEAR_DISTANCE_CLAMPED:
2624
+ case AL_EXPONENT_DISTANCE:
2625
+ case AL_EXPONENT_DISTANCE_CLAMPED:
2626
+ ctx->distance_model = model;
2627
+ context_needs_recalc(ctx);
2628
+ return;
2629
+ default: break;
2630
+ }
2631
+ set_al_error(ctx, AL_INVALID_ENUM);
2632
+ }
2633
+ ENTRYPOINTVOID(alDistanceModel,(ALenum model),(model))
2634
+
2635
+
2636
+ static void _alEnable(const ALenum capability)
2637
+ {
2638
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in core OpenAL 1.1 uses this */
2639
+ }
2640
+ ENTRYPOINTVOID(alEnable,(ALenum capability),(capability))
2641
+
2642
+
2643
+ static void _alDisable(const ALenum capability)
2644
+ {
2645
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in core OpenAL 1.1 uses this */
2646
+ }
2647
+ ENTRYPOINTVOID(alDisable,(ALenum capability),(capability))
2648
+
2649
+
2650
+ static ALboolean _alIsEnabled(const ALenum capability)
2651
+ {
2652
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in core OpenAL 1.1 uses this */
2653
+ return AL_FALSE;
2654
+ }
2655
+ ENTRYPOINT(ALboolean,alIsEnabled,(ALenum capability),(capability))
2656
+
2657
+ static const ALchar *_alGetString(const ALenum param)
2658
+ {
2659
+ switch (param) {
2660
+ case AL_EXTENSIONS: {
2661
+ #define AL_EXTENSION_ITEM(ext) " " #ext
2662
+ static ALchar al_extensions_string[] = AL_EXTENSION_ITEMS;
2663
+ #undef AL_EXTENSION_ITEM
2664
+ return al_extensions_string + 1; /* skip that first space char */
2665
+ }
2666
+
2667
+ case AL_VERSION: return OPENAL_VERSION_STRING;
2668
+ case AL_RENDERER: return OPENAL_RENDERER_STRING;
2669
+ case AL_VENDOR: return OPENAL_VENDOR_STRING;
2670
+ case AL_NO_ERROR: return "AL_NO_ERROR";
2671
+ case AL_INVALID_NAME: return "AL_INVALID_NAME";
2672
+ case AL_INVALID_ENUM: return "AL_INVALID_ENUM";
2673
+ case AL_INVALID_VALUE: return "AL_INVALID_VALUE";
2674
+ case AL_INVALID_OPERATION: return "AL_INVALID_OPERATION";
2675
+ case AL_OUT_OF_MEMORY: return "AL_OUT_OF_MEMORY";
2676
+
2677
+ default: break;
2678
+ }
2679
+
2680
+ FIXME("other enums that should report as strings?");
2681
+ set_al_error(get_current_context(), AL_INVALID_ENUM);
2682
+
2683
+ return NULL;
2684
+ }
2685
+ ENTRYPOINT(const ALchar *,alGetString,(const ALenum param),(param))
2686
+
2687
+ static void _alGetBooleanv(const ALenum param, ALboolean *values)
2688
+ {
2689
+ ALCcontext *ctx = get_current_context();
2690
+ if (!ctx) {
2691
+ set_al_error(ctx, AL_INVALID_OPERATION);
2692
+ return;
2693
+ }
2694
+
2695
+ if (!values) return; /* legal no-op */
2696
+
2697
+ /* nothing in core OpenAL 1.1 uses this */
2698
+ set_al_error(ctx, AL_INVALID_ENUM);
2699
+ }
2700
+ ENTRYPOINTVOID(alGetBooleanv,(ALenum param, ALboolean *values),(param,values))
2701
+
2702
+ static void _alGetIntegerv(const ALenum param, ALint *values)
2703
+ {
2704
+ ALCcontext *ctx = get_current_context();
2705
+ if (!ctx) {
2706
+ set_al_error(ctx, AL_INVALID_OPERATION);
2707
+ return;
2708
+ }
2709
+
2710
+ if (!values) return; /* legal no-op */
2711
+
2712
+ switch (param) {
2713
+ case AL_DISTANCE_MODEL: *values = (ALint) ctx->distance_model; break;
2714
+ default: set_al_error(ctx, AL_INVALID_ENUM); break;
2715
+ }
2716
+ }
2717
+ ENTRYPOINTVOID(alGetIntegerv,(ALenum param, ALint *values),(param,values))
2718
+
2719
+ static void _alGetFloatv(const ALenum param, ALfloat *values)
2720
+ {
2721
+ ALCcontext *ctx = get_current_context();
2722
+ if (!ctx) {
2723
+ set_al_error(ctx, AL_INVALID_OPERATION);
2724
+ return;
2725
+ }
2726
+
2727
+ if (!values) return; /* legal no-op */
2728
+
2729
+ switch (param) {
2730
+ case AL_DOPPLER_FACTOR: *values = ctx->doppler_factor; break;
2731
+ case AL_DOPPLER_VELOCITY: *values = ctx->doppler_velocity; break;
2732
+ case AL_SPEED_OF_SOUND: *values = ctx->speed_of_sound; break;
2733
+ default: set_al_error(ctx, AL_INVALID_ENUM); break;
2734
+ }
2735
+ }
2736
+ ENTRYPOINTVOID(alGetFloatv,(ALenum param, ALfloat *values),(param,values))
2737
+
2738
+ static void _alGetDoublev(const ALenum param, ALdouble *values)
2739
+ {
2740
+ ALCcontext *ctx = get_current_context();
2741
+ if (!ctx) {
2742
+ set_al_error(ctx, AL_INVALID_OPERATION);
2743
+ return;
2744
+ }
2745
+
2746
+ if (!values) return; /* legal no-op */
2747
+
2748
+ /* nothing in core OpenAL 1.1 uses this */
2749
+ set_al_error(ctx, AL_INVALID_ENUM);
2750
+ }
2751
+ ENTRYPOINTVOID(alGetDoublev,(ALenum param, ALdouble *values),(param,values))
2752
+
2753
+ /* no api lock; just passes through to the real api */
2754
+ ALboolean alGetBoolean(ALenum param)
2755
+ {
2756
+ ALboolean retval = AL_FALSE;
2757
+ alGetBooleanv(param, &retval);
2758
+ return retval;
2759
+ }
2760
+
2761
+ /* no api lock; just passes through to the real api */
2762
+ ALint alGetInteger(ALenum param)
2763
+ {
2764
+ ALint retval = 0;
2765
+ alGetIntegerv(param, &retval);
2766
+ return retval;
2767
+ }
2768
+
2769
+ /* no api lock; just passes through to the real api */
2770
+ ALfloat alGetFloat(ALenum param)
2771
+ {
2772
+ ALfloat retval = 0.0f;
2773
+ alGetFloatv(param, &retval);
2774
+ return retval;
2775
+ }
2776
+
2777
+ /* no api lock; just passes through to the real api */
2778
+ ALdouble alGetDouble(ALenum param)
2779
+ {
2780
+ ALdouble retval = 0.0f;
2781
+ alGetDoublev(param, &retval);
2782
+ return retval;
2783
+ }
2784
+
2785
+ static ALenum _alGetError(void)
2786
+ {
2787
+ ALCcontext *ctx = get_current_context();
2788
+ ALenum *perr = ctx ? &ctx->error : &null_context_error;
2789
+ const ALenum retval = *perr;
2790
+ *perr = AL_NO_ERROR;
2791
+ return retval;
2792
+ }
2793
+ ENTRYPOINT(ALenum,alGetError,(void),())
2794
+
2795
+ /* no api lock; immutable (unless we start having contexts with different extensions) */
2796
+ ALboolean alIsExtensionPresent(const ALchar *extname)
2797
+ {
2798
+ #define AL_EXTENSION_ITEM(ext) if (SDL_strcasecmp(extname, #ext) == 0) { return AL_TRUE; }
2799
+ AL_EXTENSION_ITEMS
2800
+ #undef AL_EXTENSION_ITEM
2801
+ return AL_FALSE;
2802
+ }
2803
+
2804
+ static void *_alGetProcAddress(const ALchar *funcname)
2805
+ {
2806
+ ALCcontext *ctx = get_current_context();
2807
+ FIXME("fail if ctx == NULL?");
2808
+ if (!funcname) {
2809
+ set_al_error(ctx, AL_INVALID_VALUE);
2810
+ return NULL;
2811
+ }
2812
+
2813
+ #define FN_TEST(fn) if (SDL_strcmp(funcname, #fn) == 0) return (void *) fn
2814
+ FN_TEST(alDopplerFactor);
2815
+ FN_TEST(alDopplerVelocity);
2816
+ FN_TEST(alSpeedOfSound);
2817
+ FN_TEST(alDistanceModel);
2818
+ FN_TEST(alEnable);
2819
+ FN_TEST(alDisable);
2820
+ FN_TEST(alIsEnabled);
2821
+ FN_TEST(alGetString);
2822
+ FN_TEST(alGetBooleanv);
2823
+ FN_TEST(alGetIntegerv);
2824
+ FN_TEST(alGetFloatv);
2825
+ FN_TEST(alGetDoublev);
2826
+ FN_TEST(alGetBoolean);
2827
+ FN_TEST(alGetInteger);
2828
+ FN_TEST(alGetFloat);
2829
+ FN_TEST(alGetDouble);
2830
+ FN_TEST(alGetError);
2831
+ FN_TEST(alIsExtensionPresent);
2832
+ FN_TEST(alGetProcAddress);
2833
+ FN_TEST(alGetEnumValue);
2834
+ FN_TEST(alListenerf);
2835
+ FN_TEST(alListener3f);
2836
+ FN_TEST(alListenerfv);
2837
+ FN_TEST(alListeneri);
2838
+ FN_TEST(alListener3i);
2839
+ FN_TEST(alListeneriv);
2840
+ FN_TEST(alGetListenerf);
2841
+ FN_TEST(alGetListener3f);
2842
+ FN_TEST(alGetListenerfv);
2843
+ FN_TEST(alGetListeneri);
2844
+ FN_TEST(alGetListener3i);
2845
+ FN_TEST(alGetListeneriv);
2846
+ FN_TEST(alGenSources);
2847
+ FN_TEST(alDeleteSources);
2848
+ FN_TEST(alIsSource);
2849
+ FN_TEST(alSourcef);
2850
+ FN_TEST(alSource3f);
2851
+ FN_TEST(alSourcefv);
2852
+ FN_TEST(alSourcei);
2853
+ FN_TEST(alSource3i);
2854
+ FN_TEST(alSourceiv);
2855
+ FN_TEST(alGetSourcef);
2856
+ FN_TEST(alGetSource3f);
2857
+ FN_TEST(alGetSourcefv);
2858
+ FN_TEST(alGetSourcei);
2859
+ FN_TEST(alGetSource3i);
2860
+ FN_TEST(alGetSourceiv);
2861
+ FN_TEST(alSourcePlayv);
2862
+ FN_TEST(alSourceStopv);
2863
+ FN_TEST(alSourceRewindv);
2864
+ FN_TEST(alSourcePausev);
2865
+ FN_TEST(alSourcePlay);
2866
+ FN_TEST(alSourceStop);
2867
+ FN_TEST(alSourceRewind);
2868
+ FN_TEST(alSourcePause);
2869
+ FN_TEST(alSourceQueueBuffers);
2870
+ FN_TEST(alSourceUnqueueBuffers);
2871
+ FN_TEST(alGenBuffers);
2872
+ FN_TEST(alDeleteBuffers);
2873
+ FN_TEST(alIsBuffer);
2874
+ FN_TEST(alBufferData);
2875
+ FN_TEST(alBufferf);
2876
+ FN_TEST(alBuffer3f);
2877
+ FN_TEST(alBufferfv);
2878
+ FN_TEST(alBufferi);
2879
+ FN_TEST(alBuffer3i);
2880
+ FN_TEST(alBufferiv);
2881
+ FN_TEST(alGetBufferf);
2882
+ FN_TEST(alGetBuffer3f);
2883
+ FN_TEST(alGetBufferfv);
2884
+ FN_TEST(alGetBufferi);
2885
+ FN_TEST(alGetBuffer3i);
2886
+ FN_TEST(alGetBufferiv);
2887
+ #undef FN_TEST
2888
+
2889
+ set_al_error(ctx, ALC_INVALID_VALUE);
2890
+ return NULL;
2891
+ }
2892
+ ENTRYPOINT(void *,alGetProcAddress,(const ALchar *funcname),(funcname))
2893
+
2894
+ static ALenum _alGetEnumValue(const ALchar *enumname)
2895
+ {
2896
+ ALCcontext *ctx = get_current_context();
2897
+ FIXME("fail if ctx == NULL?");
2898
+ if (!enumname) {
2899
+ set_al_error(ctx, AL_INVALID_VALUE);
2900
+ return AL_NONE;
2901
+ }
2902
+
2903
+ #define ENUM_TEST(en) if (SDL_strcmp(enumname, #en) == 0) return en
2904
+ ENUM_TEST(AL_NONE);
2905
+ ENUM_TEST(AL_FALSE);
2906
+ ENUM_TEST(AL_TRUE);
2907
+ ENUM_TEST(AL_SOURCE_RELATIVE);
2908
+ ENUM_TEST(AL_CONE_INNER_ANGLE);
2909
+ ENUM_TEST(AL_CONE_OUTER_ANGLE);
2910
+ ENUM_TEST(AL_PITCH);
2911
+ ENUM_TEST(AL_POSITION);
2912
+ ENUM_TEST(AL_DIRECTION);
2913
+ ENUM_TEST(AL_VELOCITY);
2914
+ ENUM_TEST(AL_LOOPING);
2915
+ ENUM_TEST(AL_BUFFER);
2916
+ ENUM_TEST(AL_GAIN);
2917
+ ENUM_TEST(AL_MIN_GAIN);
2918
+ ENUM_TEST(AL_MAX_GAIN);
2919
+ ENUM_TEST(AL_ORIENTATION);
2920
+ ENUM_TEST(AL_SOURCE_STATE);
2921
+ ENUM_TEST(AL_INITIAL);
2922
+ ENUM_TEST(AL_PLAYING);
2923
+ ENUM_TEST(AL_PAUSED);
2924
+ ENUM_TEST(AL_STOPPED);
2925
+ ENUM_TEST(AL_BUFFERS_QUEUED);
2926
+ ENUM_TEST(AL_BUFFERS_PROCESSED);
2927
+ ENUM_TEST(AL_REFERENCE_DISTANCE);
2928
+ ENUM_TEST(AL_ROLLOFF_FACTOR);
2929
+ ENUM_TEST(AL_CONE_OUTER_GAIN);
2930
+ ENUM_TEST(AL_MAX_DISTANCE);
2931
+ ENUM_TEST(AL_SEC_OFFSET);
2932
+ ENUM_TEST(AL_SAMPLE_OFFSET);
2933
+ ENUM_TEST(AL_BYTE_OFFSET);
2934
+ ENUM_TEST(AL_SOURCE_TYPE);
2935
+ ENUM_TEST(AL_STATIC);
2936
+ ENUM_TEST(AL_STREAMING);
2937
+ ENUM_TEST(AL_UNDETERMINED);
2938
+ ENUM_TEST(AL_FORMAT_MONO8);
2939
+ ENUM_TEST(AL_FORMAT_MONO16);
2940
+ ENUM_TEST(AL_FORMAT_STEREO8);
2941
+ ENUM_TEST(AL_FORMAT_STEREO16);
2942
+ ENUM_TEST(AL_FREQUENCY);
2943
+ ENUM_TEST(AL_BITS);
2944
+ ENUM_TEST(AL_CHANNELS);
2945
+ ENUM_TEST(AL_SIZE);
2946
+ ENUM_TEST(AL_UNUSED);
2947
+ ENUM_TEST(AL_PENDING);
2948
+ ENUM_TEST(AL_PROCESSED);
2949
+ ENUM_TEST(AL_NO_ERROR);
2950
+ ENUM_TEST(AL_INVALID_NAME);
2951
+ ENUM_TEST(AL_INVALID_ENUM);
2952
+ ENUM_TEST(AL_INVALID_VALUE);
2953
+ ENUM_TEST(AL_INVALID_OPERATION);
2954
+ ENUM_TEST(AL_OUT_OF_MEMORY);
2955
+ ENUM_TEST(AL_VENDOR);
2956
+ ENUM_TEST(AL_VERSION);
2957
+ ENUM_TEST(AL_RENDERER);
2958
+ ENUM_TEST(AL_EXTENSIONS);
2959
+ ENUM_TEST(AL_DOPPLER_FACTOR);
2960
+ ENUM_TEST(AL_DOPPLER_VELOCITY);
2961
+ ENUM_TEST(AL_SPEED_OF_SOUND);
2962
+ ENUM_TEST(AL_DISTANCE_MODEL);
2963
+ ENUM_TEST(AL_INVERSE_DISTANCE);
2964
+ ENUM_TEST(AL_INVERSE_DISTANCE_CLAMPED);
2965
+ ENUM_TEST(AL_LINEAR_DISTANCE);
2966
+ ENUM_TEST(AL_LINEAR_DISTANCE_CLAMPED);
2967
+ ENUM_TEST(AL_EXPONENT_DISTANCE);
2968
+ ENUM_TEST(AL_EXPONENT_DISTANCE_CLAMPED);
2969
+ ENUM_TEST(AL_FORMAT_MONO_FLOAT32);
2970
+ ENUM_TEST(AL_FORMAT_STEREO_FLOAT32);
2971
+ #undef ENUM_TEST
2972
+
2973
+ set_al_error(ctx, AL_INVALID_VALUE);
2974
+ return AL_NONE;
2975
+ }
2976
+ ENTRYPOINT(ALenum,alGetEnumValue,(const ALchar *enumname),(enumname))
2977
+
2978
+ static void _alListenerfv(const ALenum param, const ALfloat *values)
2979
+ {
2980
+ ALCcontext *ctx = get_current_context();
2981
+ if (!ctx) {
2982
+ set_al_error(ctx, AL_INVALID_OPERATION);
2983
+ } else if (!values) {
2984
+ set_al_error(ctx, AL_INVALID_VALUE);
2985
+ } else {
2986
+ ALboolean recalc = AL_TRUE;
2987
+ switch (param) {
2988
+ case AL_GAIN:
2989
+ ctx->listener.gain = *values;
2990
+ break;
2991
+
2992
+ case AL_POSITION:
2993
+ SDL_memcpy(ctx->listener.position, values, sizeof (*values) * 3);
2994
+ break;
2995
+
2996
+ case AL_VELOCITY:
2997
+ SDL_memcpy(ctx->listener.velocity, values, sizeof (*values) * 3);
2998
+ break;
2999
+
3000
+ case AL_ORIENTATION:
3001
+ SDL_memcpy(&ctx->listener.orientation[0], &values[0], sizeof (*values) * 3);
3002
+ SDL_memcpy(&ctx->listener.orientation[4], &values[3], sizeof (*values) * 3);
3003
+ break;
3004
+
3005
+ default:
3006
+ recalc = AL_FALSE;
3007
+ set_al_error(ctx, AL_INVALID_ENUM);
3008
+ break;
3009
+ }
3010
+
3011
+ if (recalc) {
3012
+ context_needs_recalc(ctx);
3013
+ }
3014
+ }
3015
+ }
3016
+ ENTRYPOINTVOID(alListenerfv,(ALenum param, const ALfloat *values),(param,values))
3017
+
3018
+ static void _alListenerf(const ALenum param, const ALfloat value)
3019
+ {
3020
+ switch (param) {
3021
+ case AL_GAIN: _alListenerfv(param, &value); break;
3022
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
3023
+ }
3024
+ }
3025
+ ENTRYPOINTVOID(alListenerf,(ALenum param, ALfloat value),(param,value))
3026
+
3027
+ static void _alListener3f(const ALenum param, const ALfloat value1, const ALfloat value2, const ALfloat value3)
3028
+ {
3029
+ switch (param) {
3030
+ case AL_POSITION:
3031
+ case AL_VELOCITY: {
3032
+ const ALfloat values[3] = { value1, value2, value3 };
3033
+ _alListenerfv(param, values);
3034
+ break;
3035
+ }
3036
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
3037
+ }
3038
+ }
3039
+ ENTRYPOINTVOID(alListener3f,(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3),(param,value1,value2,value3))
3040
+
3041
+ static void _alListeneriv(const ALenum param, const ALint *values)
3042
+ {
3043
+ ALCcontext *ctx = get_current_context();
3044
+ if (!ctx) {
3045
+ set_al_error(ctx, AL_INVALID_OPERATION);
3046
+ } else if (!values) {
3047
+ set_al_error(ctx, AL_INVALID_VALUE);
3048
+ } else {
3049
+ ALboolean recalc = AL_TRUE;
3050
+ FIXME("Not atomic vs the mixer thread"); /* maybe have a latching system? */
3051
+ switch (param) {
3052
+ case AL_POSITION:
3053
+ ctx->listener.position[0] = (ALfloat) values[0];
3054
+ ctx->listener.position[1] = (ALfloat) values[1];
3055
+ ctx->listener.position[2] = (ALfloat) values[2];
3056
+ break;
3057
+
3058
+ case AL_VELOCITY:
3059
+ ctx->listener.velocity[0] = (ALfloat) values[0];
3060
+ ctx->listener.velocity[1] = (ALfloat) values[1];
3061
+ ctx->listener.velocity[2] = (ALfloat) values[2];
3062
+ break;
3063
+
3064
+ case AL_ORIENTATION:
3065
+ ctx->listener.orientation[0] = (ALfloat) values[0];
3066
+ ctx->listener.orientation[1] = (ALfloat) values[1];
3067
+ ctx->listener.orientation[2] = (ALfloat) values[2];
3068
+ ctx->listener.orientation[4] = (ALfloat) values[3];
3069
+ ctx->listener.orientation[5] = (ALfloat) values[4];
3070
+ ctx->listener.orientation[6] = (ALfloat) values[5];
3071
+ break;
3072
+
3073
+ default:
3074
+ recalc = AL_FALSE;
3075
+ set_al_error(ctx, AL_INVALID_ENUM);
3076
+ break;
3077
+ }
3078
+
3079
+ if (recalc) {
3080
+ context_needs_recalc(ctx);
3081
+ }
3082
+ }
3083
+ }
3084
+ ENTRYPOINTVOID(alListeneriv,(ALenum param, const ALint *values),(param,values))
3085
+
3086
+ static void _alListeneri(const ALenum param, const ALint value)
3087
+ {
3088
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in AL 1.1 uses this */
3089
+ }
3090
+ ENTRYPOINTVOID(alListeneri,(ALenum param, ALint value),(param,value))
3091
+
3092
+ static void _alListener3i(const ALenum param, const ALint value1, const ALint value2, const ALint value3)
3093
+ {
3094
+ switch (param) {
3095
+ case AL_POSITION:
3096
+ case AL_VELOCITY: {
3097
+ const ALint values[3] = { value1, value2, value3 };
3098
+ _alListeneriv(param, values);
3099
+ break;
3100
+ }
3101
+ default:
3102
+ set_al_error(get_current_context(), AL_INVALID_ENUM);
3103
+ break;
3104
+ }
3105
+ }
3106
+ ENTRYPOINTVOID(alListener3i,(ALenum param, ALint value1, ALint value2, ALint value3),(param,value1,value2,value3))
3107
+
3108
+ static void _alGetListenerfv(const ALenum param, ALfloat *values)
3109
+ {
3110
+ ALCcontext *ctx = get_current_context();
3111
+ if (!ctx) {
3112
+ set_al_error(ctx, AL_INVALID_OPERATION);
3113
+ return;
3114
+ }
3115
+
3116
+ if (!values) return; /* legal no-op */
3117
+
3118
+ switch (param) {
3119
+ case AL_GAIN:
3120
+ *values = ctx->listener.gain;
3121
+ break;
3122
+
3123
+ case AL_POSITION:
3124
+ SDL_memcpy(values, ctx->listener.position, sizeof (ALfloat) * 3);
3125
+ break;
3126
+
3127
+ case AL_VELOCITY:
3128
+ SDL_memcpy(values, ctx->listener.velocity, sizeof (ALfloat) * 3);
3129
+ break;
3130
+
3131
+ case AL_ORIENTATION:
3132
+ SDL_memcpy(&values[0], &ctx->listener.orientation[0], sizeof (ALfloat) * 3);
3133
+ SDL_memcpy(&values[3], &ctx->listener.orientation[4], sizeof (ALfloat) * 3);
3134
+ break;
3135
+
3136
+ default: set_al_error(ctx, AL_INVALID_ENUM); break;
3137
+ }
3138
+ }
3139
+ ENTRYPOINTVOID(alGetListenerfv,(ALenum param, ALfloat *values),(param,values))
3140
+
3141
+ static void _alGetListenerf(const ALenum param, ALfloat *value)
3142
+ {
3143
+ switch (param) {
3144
+ case AL_GAIN: _alGetListenerfv(param, value); break;
3145
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
3146
+ }
3147
+ }
3148
+ ENTRYPOINTVOID(alGetListenerf,(ALenum param, ALfloat *value),(param,value))
3149
+
3150
+
3151
+ static void _alGetListener3f(const ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
3152
+ {
3153
+ ALfloat values[3];
3154
+ switch (param) {
3155
+ case AL_POSITION:
3156
+ case AL_VELOCITY:
3157
+ _alGetListenerfv(param, values);
3158
+ if (value1) *value1 = values[0];
3159
+ if (value2) *value2 = values[1];
3160
+ if (value3) *value3 = values[2];
3161
+ break;
3162
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
3163
+ }
3164
+ }
3165
+ ENTRYPOINTVOID(alGetListener3f,(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3),(param,value1,value2,value3))
3166
+
3167
+
3168
+ static void _alGetListeneri(const ALenum param, ALint *value)
3169
+ {
3170
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in AL 1.1 uses this */
3171
+ }
3172
+ ENTRYPOINTVOID(alGetListeneri,(ALenum param, ALint *value),(param,value))
3173
+
3174
+
3175
+ static void _alGetListeneriv(const ALenum param, ALint *values)
3176
+ {
3177
+ ALCcontext *ctx = get_current_context();
3178
+ if (!ctx) {
3179
+ set_al_error(ctx, AL_INVALID_OPERATION);
3180
+ return;
3181
+ }
3182
+
3183
+ if (!values) return; /* legal no-op */
3184
+
3185
+ switch (param) {
3186
+ case AL_POSITION:
3187
+ values[0] = (ALint) ctx->listener.position[0];
3188
+ values[1] = (ALint) ctx->listener.position[1];
3189
+ values[2] = (ALint) ctx->listener.position[2];
3190
+ break;
3191
+
3192
+ case AL_VELOCITY:
3193
+ values[0] = (ALint) ctx->listener.velocity[0];
3194
+ values[1] = (ALint) ctx->listener.velocity[1];
3195
+ values[2] = (ALint) ctx->listener.velocity[2];
3196
+ break;
3197
+
3198
+ case AL_ORIENTATION:
3199
+ values[0] = (ALint) ctx->listener.orientation[0];
3200
+ values[1] = (ALint) ctx->listener.orientation[1];
3201
+ values[2] = (ALint) ctx->listener.orientation[2];
3202
+ values[3] = (ALint) ctx->listener.orientation[4];
3203
+ values[4] = (ALint) ctx->listener.orientation[5];
3204
+ values[5] = (ALint) ctx->listener.orientation[6];
3205
+ break;
3206
+
3207
+ default: set_al_error(ctx, AL_INVALID_ENUM); break;
3208
+ }
3209
+ }
3210
+ ENTRYPOINTVOID(alGetListeneriv,(ALenum param, ALint *values),(param,values))
3211
+
3212
+ static void _alGetListener3i(const ALenum param, ALint *value1, ALint *value2, ALint *value3)
3213
+ {
3214
+ ALint values[3];
3215
+ switch (param) {
3216
+ case AL_POSITION:
3217
+ case AL_VELOCITY:
3218
+ _alGetListeneriv(param, values);
3219
+ if (value1) *value1 = values[0];
3220
+ if (value2) *value2 = values[1];
3221
+ if (value3) *value3 = values[2];
3222
+ break;
3223
+
3224
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
3225
+ }
3226
+ }
3227
+ ENTRYPOINTVOID(alGetListener3i,(ALenum param, ALint *value1, ALint *value2, ALint *value3),(param,value1,value2,value3))
3228
+
3229
+ /* !!! FIXME: buffers and sources use almost identical code for blocks */
3230
+ static void _alGenSources(const ALsizei n, ALuint *names)
3231
+ {
3232
+ ALCcontext *ctx = get_current_context();
3233
+ ALboolean out_of_memory = AL_FALSE;
3234
+ ALsizei totalblocks;
3235
+ ALsource *stackobjs[16];
3236
+ ALsource **objects = stackobjs;
3237
+ ALsizei found = 0;
3238
+ ALsizei block_offset = 0;
3239
+ ALsizei blocki;
3240
+ ALsizei i;
3241
+
3242
+ if (!ctx) {
3243
+ set_al_error(ctx, AL_INVALID_OPERATION);
3244
+ return;
3245
+ }
3246
+
3247
+ if (n <= SDL_arraysize(stackobjs)) {
3248
+ SDL_memset(stackobjs, '\0', sizeof (ALsource *) * n);
3249
+ } else {
3250
+ objects = (ALsource **) SDL_calloc(n, sizeof (ALsource *));
3251
+ if (!objects) {
3252
+ set_al_error(ctx, AL_OUT_OF_MEMORY);
3253
+ return;
3254
+ }
3255
+ }
3256
+
3257
+ totalblocks = ctx->num_source_blocks;
3258
+ for (blocki = 0; blocki < totalblocks; blocki++) {
3259
+ SourceBlock *block = ctx->source_blocks[blocki];
3260
+ block->tmp = 0;
3261
+ if (block->used < SDL_arraysize(block->sources)) { /* skip if full */
3262
+ for (i = 0; i < SDL_arraysize(block->sources); i++) {
3263
+ /* if a playing source was deleted, it will still be marked mixer_accessible
3264
+ until the mixer thread shuffles it out. Until then, the source isn't
3265
+ available for reuse. */
3266
+ if (!block->sources[i].allocated && !SDL_AtomicGet(&block->sources[i].mixer_accessible)) {
3267
+ block->tmp++;
3268
+ objects[found] = &block->sources[i];
3269
+ names[found++] = (i + block_offset) + 1; /* +1 so it isn't zero. */
3270
+ if (found == n) {
3271
+ break;
3272
+ }
3273
+ }
3274
+ }
3275
+
3276
+ if (found == n) {
3277
+ break;
3278
+ }
3279
+ }
3280
+
3281
+ block_offset += SDL_arraysize(block->sources);
3282
+ }
3283
+
3284
+ while (found < n) { /* out of blocks? Add new ones. */
3285
+ /* ctx->source_blocks is only accessed on the API thread under a mutex, so it's safe to realloc. */
3286
+ void *ptr = SDL_realloc(ctx->source_blocks, sizeof (SourceBlock *) * (totalblocks + 1));
3287
+ SourceBlock *block;
3288
+
3289
+ if (!ptr) {
3290
+ out_of_memory = AL_TRUE;
3291
+ break;
3292
+ }
3293
+ ctx->source_blocks = (SourceBlock **) ptr;
3294
+
3295
+ block = (SourceBlock *) calloc_simd_aligned(sizeof (SourceBlock));
3296
+ if (!block) {
3297
+ out_of_memory = AL_TRUE;
3298
+ break;
3299
+ }
3300
+ ctx->source_blocks[totalblocks] = block;
3301
+ totalblocks++;
3302
+ ctx->num_source_blocks++;
3303
+
3304
+ for (i = 0; i < SDL_arraysize(block->sources); i++) {
3305
+ block->tmp++;
3306
+ objects[found] = &block->sources[i];
3307
+ names[found++] = (i + block_offset) + 1; /* +1 so it isn't zero. */
3308
+ if (found == n) {
3309
+ break;
3310
+ }
3311
+ }
3312
+ block_offset += SDL_arraysize(block->sources);
3313
+ }
3314
+
3315
+ if (out_of_memory) {
3316
+ if (objects != stackobjs) SDL_free(objects);
3317
+ SDL_memset(names, '\0', sizeof (*names) * n);
3318
+ set_al_error(ctx, AL_OUT_OF_MEMORY);
3319
+ return;
3320
+ }
3321
+
3322
+ SDL_assert(found == n); /* we should have either gotten space or bailed on alloc failure */
3323
+
3324
+ /* update the "used" field in blocks with items we are taking now. */
3325
+ found = 0;
3326
+ for (blocki = 0; found < n; blocki++) {
3327
+ SourceBlock *block = ctx->source_blocks[blocki];
3328
+ SDL_assert(blocki < totalblocks);
3329
+ const int foundhere = block->tmp;
3330
+ if (foundhere) {
3331
+ block->used += foundhere;
3332
+ found += foundhere;
3333
+ block->tmp = 0;
3334
+ }
3335
+ }
3336
+
3337
+ SDL_assert(found == n);
3338
+
3339
+ for (i = 0; i < n; i++) {
3340
+ ALsource *src = objects[i];
3341
+
3342
+ /*printf("Generated source %u\n", (unsigned int) names[i]);*/
3343
+
3344
+ SDL_assert(!src->allocated);
3345
+
3346
+ /* Make sure everything that wants to use SIMD is aligned for it. */
3347
+ SDL_assert( (((size_t) &src->position[0]) % 16) == 0 );
3348
+ SDL_assert( (((size_t) &src->velocity[0]) % 16) == 0 );
3349
+ SDL_assert( (((size_t) &src->direction[0]) % 16) == 0 );
3350
+
3351
+ SDL_zerop(src);
3352
+ SDL_AtomicSet(&src->state, AL_INITIAL);
3353
+ src->name = names[i];
3354
+ src->type = AL_UNDETERMINED;
3355
+ src->recalc = AL_TRUE;
3356
+ src->gain = 1.0f;
3357
+ src->max_gain = 1.0f;
3358
+ src->reference_distance = 1.0f;
3359
+ src->max_distance = FLT_MAX;
3360
+ src->rolloff_factor = 1.0f;
3361
+ src->pitch = 1.0f;
3362
+ src->cone_inner_angle = 360.0f;
3363
+ src->cone_outer_angle = 360.0f;
3364
+ source_needs_recalc(src);
3365
+ src->allocated = AL_TRUE; /* we officially own it. */
3366
+ }
3367
+
3368
+ if (objects != stackobjs) SDL_free(objects);
3369
+ }
3370
+ ENTRYPOINTVOID(alGenSources,(ALsizei n, ALuint *names),(n,names))
3371
+
3372
+
3373
+ static void _alDeleteSources(const ALsizei n, const ALuint *names)
3374
+ {
3375
+ ALCcontext *ctx = get_current_context();
3376
+ ALsizei i;
3377
+
3378
+ if (!ctx) {
3379
+ set_al_error(ctx, AL_INVALID_OPERATION);
3380
+ return;
3381
+ }
3382
+
3383
+ for (i = 0; i < n; i++) {
3384
+ const ALuint name = names[i];
3385
+ if (name == 0) {
3386
+ /* ignore it. */ FIXME("Spec says alDeleteBuffers() can have a zero name as a legal no-op, but this text isn't included in alDeleteSources...");
3387
+ } else {
3388
+ ALsource *source = get_source(ctx, name, NULL);
3389
+ if (!source) {
3390
+ /* "If one or more of the specified names is not valid, an AL_INVALID_NAME error will be recorded, and no objects will be deleted." */
3391
+ set_al_error(ctx, AL_INVALID_NAME);
3392
+ return;
3393
+ }
3394
+ }
3395
+ }
3396
+
3397
+ for (i = 0; i < n; i++) {
3398
+ const ALuint name = names[i];
3399
+ if (name != 0) {
3400
+ SourceBlock *block;
3401
+ ALsource *source = get_source(ctx, name, &block);
3402
+ SDL_assert(source != NULL);
3403
+
3404
+ /* "A playing source can be deleted--the source will be stopped automatically and then deleted." */
3405
+ if (!SDL_AtomicGet(&source->mixer_accessible)) {
3406
+ SDL_AtomicSet(&source->state, AL_STOPPED);
3407
+ } else {
3408
+ SDL_LockMutex(ctx->source_lock);
3409
+ SDL_AtomicSet(&source->state, AL_STOPPED); /* mixer will drop from playlist next time it sees this. */
3410
+ SDL_UnlockMutex(ctx->source_lock);
3411
+ }
3412
+ source->allocated = AL_FALSE;
3413
+ source_release_buffer_queue(ctx, source);
3414
+ if (source->buffer) {
3415
+ SDL_assert(source->type == AL_STATIC);
3416
+ (void) SDL_AtomicDecRef(&source->buffer->refcount);
3417
+ source->buffer = NULL;
3418
+ }
3419
+ if (source->stream) {
3420
+ SDL_FreeAudioStream(source->stream);
3421
+ source->stream = NULL;
3422
+ }
3423
+ block->used--;
3424
+ }
3425
+ }
3426
+ }
3427
+ ENTRYPOINTVOID(alDeleteSources,(ALsizei n, const ALuint *names),(n,names))
3428
+
3429
+ static ALboolean _alIsSource(const ALuint name)
3430
+ {
3431
+ ALCcontext *ctx = get_current_context();
3432
+ return (ctx && (get_source(ctx, name, NULL) != NULL)) ? AL_TRUE : AL_FALSE;
3433
+ }
3434
+ ENTRYPOINT(ALboolean,alIsSource,(ALuint name),(name))
3435
+
3436
+ static void _alSourcefv(const ALuint name, const ALenum param, const ALfloat *values)
3437
+ {
3438
+ ALCcontext *ctx = get_current_context();
3439
+ ALsource *src = get_source(ctx, name, NULL);
3440
+ if (!src) return;
3441
+
3442
+ switch (param) {
3443
+ case AL_GAIN: src->gain = *values; break;
3444
+ case AL_POSITION: SDL_memcpy(src->position, values, sizeof (ALfloat) * 3); break;
3445
+ case AL_VELOCITY: SDL_memcpy(src->velocity, values, sizeof (ALfloat) * 3); break;
3446
+ case AL_DIRECTION: SDL_memcpy(src->direction, values, sizeof (ALfloat) * 3); break;
3447
+ case AL_MIN_GAIN: src->min_gain = *values; break;
3448
+ case AL_MAX_GAIN: src->max_gain = *values; break;
3449
+ case AL_REFERENCE_DISTANCE: src->reference_distance = *values; break;
3450
+ case AL_ROLLOFF_FACTOR: src->rolloff_factor = *values; break;
3451
+ case AL_MAX_DISTANCE: src->max_distance = *values; break;
3452
+ case AL_PITCH: src->pitch = *values; break;
3453
+ case AL_CONE_INNER_ANGLE: src->cone_inner_angle = *values; break;
3454
+ case AL_CONE_OUTER_ANGLE: src->cone_outer_angle = *values; break;
3455
+ case AL_CONE_OUTER_GAIN: src->cone_outer_gain = *values; break;
3456
+
3457
+ case AL_SEC_OFFSET:
3458
+ case AL_SAMPLE_OFFSET:
3459
+ case AL_BYTE_OFFSET:
3460
+ source_set_offset(src, param, *values);
3461
+ break;
3462
+
3463
+ default: set_al_error(ctx, AL_INVALID_ENUM); return;
3464
+
3465
+ }
3466
+
3467
+ source_needs_recalc(src);
3468
+ }
3469
+ ENTRYPOINTVOID(alSourcefv,(ALuint name, ALenum param, const ALfloat *values),(name,param,values))
3470
+
3471
+ static void _alSourcef(const ALuint name, const ALenum param, const ALfloat value)
3472
+ {
3473
+ switch (param) {
3474
+ case AL_GAIN:
3475
+ case AL_MIN_GAIN:
3476
+ case AL_MAX_GAIN:
3477
+ case AL_REFERENCE_DISTANCE:
3478
+ case AL_ROLLOFF_FACTOR:
3479
+ case AL_MAX_DISTANCE:
3480
+ case AL_PITCH:
3481
+ case AL_CONE_INNER_ANGLE:
3482
+ case AL_CONE_OUTER_ANGLE:
3483
+ case AL_CONE_OUTER_GAIN:
3484
+ case AL_SEC_OFFSET:
3485
+ case AL_SAMPLE_OFFSET:
3486
+ case AL_BYTE_OFFSET:
3487
+ _alSourcefv(name, param, &value);
3488
+ break;
3489
+
3490
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
3491
+ }
3492
+ }
3493
+ ENTRYPOINTVOID(alSourcef,(ALuint name, ALenum param, ALfloat value),(name,param,value))
3494
+
3495
+ static void _alSource3f(const ALuint name, const ALenum param, const ALfloat value1, const ALfloat value2, const ALfloat value3)
3496
+ {
3497
+ switch (param) {
3498
+ case AL_POSITION:
3499
+ case AL_VELOCITY:
3500
+ case AL_DIRECTION: {
3501
+ const ALfloat values[3] = { value1, value2, value3 };
3502
+ _alSourcefv(name, param, values);
3503
+ break;
3504
+ }
3505
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
3506
+ }
3507
+ }
3508
+ ENTRYPOINTVOID(alSource3f,(ALuint name, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3),(name,param,value1,value2,value3))
3509
+
3510
+ static void set_source_static_buffer(ALCcontext *ctx, ALsource *src, const ALuint bufname)
3511
+ {
3512
+ const ALenum state = (const ALenum) SDL_AtomicGet(&src->state);
3513
+ if ((state == AL_PLAYING) || (state == AL_PAUSED)) {
3514
+ set_al_error(ctx, AL_INVALID_OPERATION); /* can't change buffer on playing/paused sources */
3515
+ } else {
3516
+ ALbuffer *buffer = NULL;
3517
+ if (bufname && ((buffer = get_buffer(ctx, bufname, NULL)) == NULL)) {
3518
+ set_al_error(ctx, AL_INVALID_VALUE);
3519
+ } else {
3520
+ const ALboolean must_lock = SDL_AtomicGet(&src->mixer_accessible) ? AL_TRUE : AL_FALSE;
3521
+ SDL_AudioStream *stream = NULL;
3522
+ SDL_AudioStream *freestream = NULL;
3523
+ /* We only use the stream for resampling, not for channel conversion. */
3524
+ FIXME("keep the existing stream if formats match?");
3525
+ if (buffer && (ctx->device->frequency != buffer->frequency)) {
3526
+ stream = SDL_NewAudioStream(AUDIO_F32SYS, buffer->channels, buffer->frequency, AUDIO_F32SYS, buffer->channels, ctx->device->frequency);
3527
+ if (!stream) {
3528
+ set_al_error(ctx, AL_OUT_OF_MEMORY);
3529
+ return;
3530
+ }
3531
+ FIXME("need a way to prealloc space in the stream, so the mixer doesn't have to malloc");
3532
+ }
3533
+
3534
+ /* this can happen if you alSource(AL_BUFFER) while the exact source is in the middle of mixing */
3535
+ FIXME("Double-check this lock; we shouldn't be able to reach this if the source is playing.");
3536
+ if (must_lock) {
3537
+ SDL_LockMutex(ctx->source_lock);
3538
+ }
3539
+
3540
+ if (src->buffer != buffer) {
3541
+ if (src->buffer) {
3542
+ (void) SDL_AtomicDecRef(&src->buffer->refcount);
3543
+ }
3544
+ if (buffer) {
3545
+ SDL_AtomicIncRef(&buffer->refcount);
3546
+ }
3547
+ src->buffer = buffer;
3548
+ }
3549
+
3550
+ src->type = buffer ? AL_STATIC : AL_UNDETERMINED;
3551
+ src->queue_channels = buffer ? buffer->channels : 0;
3552
+ src->queue_frequency = 0;
3553
+
3554
+ source_release_buffer_queue(ctx, src);
3555
+
3556
+ if (src->stream != stream) {
3557
+ freestream = src->stream; /* free this after unlocking. */
3558
+ src->stream = stream;
3559
+ }
3560
+
3561
+ if (must_lock) {
3562
+ SDL_UnlockMutex(ctx->source_lock);
3563
+ }
3564
+
3565
+ if (freestream) {
3566
+ SDL_FreeAudioStream(freestream);
3567
+ }
3568
+ }
3569
+ }
3570
+ }
3571
+
3572
+ static void _alSourceiv(const ALuint name, const ALenum param, const ALint *values)
3573
+ {
3574
+ ALCcontext *ctx = get_current_context();
3575
+ ALsource *src = get_source(ctx, name, NULL);
3576
+ if (!src) return;
3577
+
3578
+ switch (param) {
3579
+ case AL_BUFFER: set_source_static_buffer(ctx, src, (ALuint) *values); break;
3580
+ case AL_SOURCE_RELATIVE: src->source_relative = *values ? AL_TRUE : AL_FALSE; break;
3581
+ case AL_LOOPING: src->looping = *values ? AL_TRUE : AL_FALSE; break;
3582
+ case AL_REFERENCE_DISTANCE: src->reference_distance = (ALfloat) *values; break;
3583
+ case AL_ROLLOFF_FACTOR: src->rolloff_factor = (ALfloat) *values; break;
3584
+ case AL_MAX_DISTANCE: src->max_distance = (ALfloat) *values; break;
3585
+ case AL_CONE_INNER_ANGLE: src->cone_inner_angle = (ALfloat) *values; break;
3586
+ case AL_CONE_OUTER_ANGLE: src->cone_outer_angle = (ALfloat) *values; break;
3587
+
3588
+ case AL_DIRECTION:
3589
+ src->direction[0] = (ALfloat) values[0];
3590
+ src->direction[1] = (ALfloat) values[1];
3591
+ src->direction[2] = (ALfloat) values[2];
3592
+ break;
3593
+
3594
+ case AL_SEC_OFFSET:
3595
+ case AL_SAMPLE_OFFSET:
3596
+ case AL_BYTE_OFFSET:
3597
+ source_set_offset(src, param, (ALfloat)*values);
3598
+ break;
3599
+
3600
+ default: set_al_error(ctx, AL_INVALID_ENUM); return;
3601
+ }
3602
+
3603
+ source_needs_recalc(src);
3604
+ }
3605
+ ENTRYPOINTVOID(alSourceiv,(ALuint name, ALenum param, const ALint *values),(name,param,values))
3606
+
3607
+ static void _alSourcei(const ALuint name, const ALenum param, const ALint value)
3608
+ {
3609
+ switch (param) {
3610
+ case AL_SOURCE_RELATIVE:
3611
+ case AL_LOOPING:
3612
+ case AL_BUFFER:
3613
+ case AL_REFERENCE_DISTANCE:
3614
+ case AL_ROLLOFF_FACTOR:
3615
+ case AL_MAX_DISTANCE:
3616
+ case AL_CONE_INNER_ANGLE:
3617
+ case AL_CONE_OUTER_ANGLE:
3618
+ case AL_SEC_OFFSET:
3619
+ case AL_SAMPLE_OFFSET:
3620
+ case AL_BYTE_OFFSET:
3621
+ _alSourceiv(name, param, &value);
3622
+ break;
3623
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
3624
+ }
3625
+ }
3626
+ ENTRYPOINTVOID(alSourcei,(ALuint name, ALenum param, ALint value),(name,param,value))
3627
+
3628
+ static void _alSource3i(const ALuint name, const ALenum param, const ALint value1, const ALint value2, const ALint value3)
3629
+ {
3630
+ switch (param) {
3631
+ case AL_DIRECTION: {
3632
+ const ALint values[3] = { (ALint) value1, (ALint) value2, (ALint) value3 };
3633
+ _alSourceiv(name, param, values);
3634
+ break;
3635
+ }
3636
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
3637
+ }
3638
+ }
3639
+ ENTRYPOINTVOID(alSource3i,(ALuint name, ALenum param, ALint value1, ALint value2, ALint value3),(name,param,value1,value2,value3))
3640
+
3641
+ static void _alGetSourcefv(const ALuint name, const ALenum param, ALfloat *values)
3642
+ {
3643
+ ALCcontext *ctx = get_current_context();
3644
+ ALsource *src = get_source(ctx, name, NULL);
3645
+ if (!src) return;
3646
+
3647
+ switch (param) {
3648
+ case AL_GAIN: *values = src->gain; break;
3649
+ case AL_POSITION: SDL_memcpy(values, src->position, sizeof (ALfloat) * 3); break;
3650
+ case AL_VELOCITY: SDL_memcpy(values, src->velocity, sizeof (ALfloat) * 3); break;
3651
+ case AL_DIRECTION: SDL_memcpy(values, src->direction, sizeof (ALfloat) * 3); break;
3652
+ case AL_MIN_GAIN: *values = src->min_gain; break;
3653
+ case AL_MAX_GAIN: *values = src->max_gain; break;
3654
+ case AL_REFERENCE_DISTANCE: *values = src->reference_distance; break;
3655
+ case AL_ROLLOFF_FACTOR: *values = src->rolloff_factor; break;
3656
+ case AL_MAX_DISTANCE: *values = src->max_distance; break;
3657
+ case AL_PITCH: *values = src->pitch; break;
3658
+ case AL_CONE_INNER_ANGLE: *values = src->cone_inner_angle; break;
3659
+ case AL_CONE_OUTER_ANGLE: *values = src->cone_outer_angle; break;
3660
+ case AL_CONE_OUTER_GAIN: *values = src->cone_outer_gain; break;
3661
+
3662
+ case AL_SEC_OFFSET:
3663
+ case AL_SAMPLE_OFFSET:
3664
+ case AL_BYTE_OFFSET:
3665
+ *values = source_get_offset(src, param);
3666
+ break;
3667
+
3668
+ default: set_al_error(ctx, AL_INVALID_ENUM); break;
3669
+ }
3670
+ }
3671
+ ENTRYPOINTVOID(alGetSourcefv,(ALuint name, ALenum param, ALfloat *values),(name,param,values))
3672
+
3673
+ static void _alGetSourcef(const ALuint name, const ALenum param, ALfloat *value)
3674
+ {
3675
+ switch (param) {
3676
+ case AL_GAIN:
3677
+ case AL_MIN_GAIN:
3678
+ case AL_MAX_GAIN:
3679
+ case AL_REFERENCE_DISTANCE:
3680
+ case AL_ROLLOFF_FACTOR:
3681
+ case AL_MAX_DISTANCE:
3682
+ case AL_PITCH:
3683
+ case AL_CONE_INNER_ANGLE:
3684
+ case AL_CONE_OUTER_ANGLE:
3685
+ case AL_CONE_OUTER_GAIN:
3686
+ case AL_SEC_OFFSET:
3687
+ case AL_SAMPLE_OFFSET:
3688
+ case AL_BYTE_OFFSET:
3689
+ _alGetSourcefv(name, param, value);
3690
+ break;
3691
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
3692
+ }
3693
+ }
3694
+ ENTRYPOINTVOID(alGetSourcef,(ALuint name, ALenum param, ALfloat *value),(name,param,value))
3695
+
3696
+ static void _alGetSource3f(const ALuint name, const ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
3697
+ {
3698
+ switch (param) {
3699
+ case AL_POSITION:
3700
+ case AL_VELOCITY:
3701
+ case AL_DIRECTION: {
3702
+ ALfloat values[3];
3703
+ _alGetSourcefv(name, param, values);
3704
+ if (value1) *value1 = values[0];
3705
+ if (value2) *value2 = values[1];
3706
+ if (value3) *value3 = values[2];
3707
+ break;
3708
+ }
3709
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
3710
+ }
3711
+ }
3712
+ ENTRYPOINTVOID(alGetSource3f,(ALuint name, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3),(name,param,value1,value2,value3))
3713
+
3714
+ static void _alGetSourceiv(const ALuint name, const ALenum param, ALint *values)
3715
+ {
3716
+ ALCcontext *ctx = get_current_context();
3717
+ ALsource *src = get_source(ctx, name, NULL);
3718
+ if (!src) return;
3719
+
3720
+ switch (param) {
3721
+ case AL_SOURCE_STATE: *values = (ALint) SDL_AtomicGet(&src->state); break;
3722
+ case AL_SOURCE_TYPE: *values = (ALint) src->type; break;
3723
+ case AL_BUFFER: *values = (ALint) (src->buffer ? src->buffer->name : 0); break;
3724
+ /* !!! FIXME: AL_BUFFERS_QUEUED is the total number of buffers pending, playing, and processed, so this is wrong. It might also have to be 1 if there's a static buffer, but I'm not sure. */
3725
+ case AL_BUFFERS_QUEUED: *values = (ALint) SDL_AtomicGet(&src->buffer_queue.num_items); break;
3726
+ case AL_BUFFERS_PROCESSED: *values = (ALint) SDL_AtomicGet(&src->buffer_queue_processed.num_items); break;
3727
+ case AL_SOURCE_RELATIVE: *values = (ALint) src->source_relative; break;
3728
+ case AL_LOOPING: *values = (ALint) src->looping; break;
3729
+ case AL_REFERENCE_DISTANCE: *values = (ALint) src->reference_distance; break;
3730
+ case AL_ROLLOFF_FACTOR: *values = (ALint) src->rolloff_factor; break;
3731
+ case AL_MAX_DISTANCE: *values = (ALint) src->max_distance; break;
3732
+ case AL_CONE_INNER_ANGLE: *values = (ALint) src->cone_inner_angle; break;
3733
+ case AL_CONE_OUTER_ANGLE: *values = (ALint) src->cone_outer_angle; break;
3734
+ case AL_DIRECTION:
3735
+ values[0] = (ALint) src->direction[0];
3736
+ values[1] = (ALint) src->direction[1];
3737
+ values[2] = (ALint) src->direction[2];
3738
+ break;
3739
+
3740
+ case AL_SEC_OFFSET:
3741
+ case AL_SAMPLE_OFFSET:
3742
+ case AL_BYTE_OFFSET:
3743
+ *values = (ALint) source_get_offset(src, param);
3744
+ break;
3745
+
3746
+ default: set_al_error(ctx, AL_INVALID_ENUM); break;
3747
+ }
3748
+ }
3749
+ ENTRYPOINTVOID(alGetSourceiv,(ALuint name, ALenum param, ALint *values),(name,param,values))
3750
+
3751
+ static void _alGetSourcei(const ALuint name, const ALenum param, ALint *value)
3752
+ {
3753
+ switch (param) {
3754
+ case AL_SOURCE_STATE:
3755
+ case AL_SOURCE_RELATIVE:
3756
+ case AL_LOOPING:
3757
+ case AL_BUFFER:
3758
+ case AL_BUFFERS_QUEUED:
3759
+ case AL_BUFFERS_PROCESSED:
3760
+ case AL_SOURCE_TYPE:
3761
+ case AL_REFERENCE_DISTANCE:
3762
+ case AL_ROLLOFF_FACTOR:
3763
+ case AL_MAX_DISTANCE:
3764
+ case AL_CONE_INNER_ANGLE:
3765
+ case AL_CONE_OUTER_ANGLE:
3766
+ case AL_SEC_OFFSET:
3767
+ case AL_SAMPLE_OFFSET:
3768
+ case AL_BYTE_OFFSET:
3769
+ _alGetSourceiv(name, param, value);
3770
+ break;
3771
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
3772
+ }
3773
+ }
3774
+ ENTRYPOINTVOID(alGetSourcei,(ALuint name, ALenum param, ALint *value),(name,param,value))
3775
+
3776
+ static void _alGetSource3i(const ALuint name, const ALenum param, ALint *value1, ALint *value2, ALint *value3)
3777
+ {
3778
+ switch (param) {
3779
+ case AL_DIRECTION: {
3780
+ ALint values[3];
3781
+ _alGetSourceiv(name, param, values);
3782
+ if (value1) *value1 = values[0];
3783
+ if (value2) *value2 = values[1];
3784
+ if (value3) *value3 = values[2];
3785
+ break;
3786
+ }
3787
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
3788
+ }
3789
+ }
3790
+ ENTRYPOINTVOID(alGetSource3i,(ALuint name, ALenum param, ALint *value1, ALint *value2, ALint *value3),(name,param,value1,value2,value3))
3791
+
3792
+ static void source_play(ALCcontext *ctx, const ALsizei n, const ALuint *names)
3793
+ {
3794
+ ALboolean failed = AL_FALSE;
3795
+ SourcePlayTodo todo;
3796
+ SourcePlayTodo *todoend = &todo;
3797
+ SourcePlayTodo *todoptr;
3798
+ void *ptr;
3799
+ ALsizei i;
3800
+
3801
+ if (n == 0) {
3802
+ return;
3803
+ } else if (!ctx) {
3804
+ set_al_error(ctx, AL_INVALID_OPERATION);
3805
+ return;
3806
+ }
3807
+
3808
+ SDL_zero(todo);
3809
+
3810
+ /* Obtain our SourcePlayTodo items upfront; if this runs out of
3811
+ memory, we won't have changed any state. The mixer thread will
3812
+ put items back in the pool when done with them, so this handoff needs
3813
+ to be atomic. */
3814
+ for (i = 0; i < n; i++) {
3815
+ SourcePlayTodo *item;
3816
+ do {
3817
+ ptr = SDL_AtomicGetPtr(&ctx->device->playback.source_todo_pool);
3818
+ item = (SourcePlayTodo *) ptr;
3819
+ if (!item) break;
3820
+ ptr = item->next;
3821
+ } while (!SDL_AtomicCASPtr(&ctx->device->playback.source_todo_pool, item, ptr));
3822
+
3823
+ if (!item) { /* allocate a new item */
3824
+ item = (SourcePlayTodo *) SDL_calloc(1, sizeof (SourcePlayTodo));
3825
+ if (!item) {
3826
+ set_al_error(ctx, AL_OUT_OF_MEMORY);
3827
+ failed = AL_TRUE;
3828
+ break;
3829
+ }
3830
+ }
3831
+
3832
+ item->next = NULL;
3833
+ todoend->next = item;
3834
+ todoend = item;
3835
+ }
3836
+
3837
+ if (failed) {
3838
+ /* put the whole new queue back in the pool for reuse later. */
3839
+ if (todo.next) {
3840
+ do {
3841
+ ptr = SDL_AtomicGetPtr(&ctx->device->playback.source_todo_pool);
3842
+ todoend->next = (SourcePlayTodo *) ptr;
3843
+ } while (!SDL_AtomicCASPtr(&ctx->device->playback.source_todo_pool, ptr, todo.next));
3844
+ }
3845
+ return;
3846
+ }
3847
+
3848
+ FIXME("What do we do if there's an invalid source in the middle of the names vector?");
3849
+ for (i = 0, todoptr = todo.next; i < n; i++) {
3850
+ const ALuint name = names[i];
3851
+ ALsource *src = get_source(ctx, name, NULL);
3852
+ if (src) {
3853
+ if (src->offset_latched) {
3854
+ src->offset_latched = AL_FALSE;
3855
+ } else if (SDL_AtomicGet(&src->state) != AL_PAUSED) {
3856
+ src->offset = 0;
3857
+ }
3858
+
3859
+ /* this used to move right to AL_STOPPED if the device is
3860
+ disconnected, but now we let the mixer thread handle that to
3861
+ avoid race conditions with marking the buffer queue
3862
+ processed, etc. Strictly speaking, ALC_EXT_disconnect
3863
+ says playing a source on a disconnected device should
3864
+ "immediately" progress to STOPPED, but I'm willing to
3865
+ say that the mixer will "immediately" move it as opposed to
3866
+ it stopping when the source would be done mixing (or worse:
3867
+ hang there forever). */
3868
+ SDL_AtomicSet(&src->state, AL_PLAYING);
3869
+
3870
+ /* Mark this as visible to the mixer. This will be set back to zero by the mixer thread when it is done with the source. */
3871
+ SDL_AtomicSet(&src->mixer_accessible, 1);
3872
+
3873
+ todoptr->source = src;
3874
+ todoptr = todoptr->next;
3875
+ }
3876
+ }
3877
+
3878
+ /* Send the list to the mixer atomically, so all sources start playing in sync!
3879
+ We're going to put these on a linked list called playlist_todo
3880
+ The mixer does an atomiccasptr to grab the current list, swapping
3881
+ in a NULL. Once it has the list, it's safe to do what it likes
3882
+ with it, as nothing else owns the pointers in that list. */
3883
+ do {
3884
+ ptr = SDL_AtomicGetPtr(&ctx->playlist_todo);
3885
+ todoend->next = ptr;
3886
+ } while (!SDL_AtomicCASPtr(&ctx->playlist_todo, ptr, todo.next));
3887
+ }
3888
+
3889
+ static void _alSourcePlay(const ALuint name)
3890
+ {
3891
+ source_play(get_current_context(), 1, &name);
3892
+ }
3893
+ ENTRYPOINTVOID(alSourcePlay,(ALuint name),(name))
3894
+
3895
+ static void _alSourcePlayv(ALsizei n, const ALuint *names)
3896
+ {
3897
+ source_play(get_current_context(), n, names);
3898
+ }
3899
+ ENTRYPOINTVOID(alSourcePlayv,(ALsizei n, const ALuint *names),(n, names))
3900
+
3901
+
3902
+ static void source_stop(ALCcontext *ctx, const ALuint name)
3903
+ {
3904
+ ALsource *src = get_source(ctx, name, NULL);
3905
+ if (src) {
3906
+ if (SDL_AtomicGet(&src->state) != AL_INITIAL) {
3907
+ const ALboolean must_lock = SDL_AtomicGet(&src->mixer_accessible) ? AL_TRUE : AL_FALSE;
3908
+ if (must_lock) {
3909
+ SDL_LockMutex(ctx->source_lock);
3910
+ }
3911
+ SDL_AtomicSet(&src->state, AL_STOPPED);
3912
+ source_mark_all_buffers_processed(src);
3913
+ if (must_lock) {
3914
+ SDL_UnlockMutex(ctx->source_lock);
3915
+ }
3916
+ }
3917
+ }
3918
+ }
3919
+
3920
+ static void source_rewind(ALCcontext *ctx, const ALuint name)
3921
+ {
3922
+ ALsource *src = get_source(ctx, name, NULL);
3923
+ if (src) {
3924
+ const ALboolean must_lock = SDL_AtomicGet(&src->mixer_accessible) ? AL_TRUE : AL_FALSE;
3925
+ if (must_lock) {
3926
+ SDL_LockMutex(ctx->source_lock);
3927
+ }
3928
+ SDL_AtomicSet(&src->state, AL_INITIAL);
3929
+ src->offset = 0;
3930
+ if (must_lock) {
3931
+ SDL_UnlockMutex(ctx->source_lock);
3932
+ }
3933
+ }
3934
+ }
3935
+
3936
+ static void source_pause(ALCcontext *ctx, const ALuint name)
3937
+ {
3938
+ ALsource *src = get_source(ctx, name, NULL);
3939
+ if (src) {
3940
+ SDL_AtomicCAS(&src->state, AL_PLAYING, AL_PAUSED);
3941
+ }
3942
+ }
3943
+
3944
+ static float source_get_offset(ALsource *src, ALenum param)
3945
+ {
3946
+ int offset = 0;
3947
+ int framesize = sizeof (float);
3948
+ int freq = 1;
3949
+ if (src->type == AL_STREAMING) {
3950
+ /* streaming: the offset counts from the first processed buffer in the queue. */
3951
+ BufferQueueItem *item = src->buffer_queue.head;
3952
+ if (item) {
3953
+ framesize = (int) (item->buffer->channels * sizeof (float));
3954
+ freq = (int) (item->buffer->frequency);
3955
+ int proc_buf = SDL_AtomicGet(&src->buffer_queue_processed.num_items);
3956
+ offset = (proc_buf * item->buffer->len + src->offset);
3957
+ }
3958
+ } else {
3959
+ framesize = (int) (src->buffer->channels * sizeof (float));
3960
+ freq = (int) src->buffer->frequency;
3961
+ offset = src->offset;
3962
+ }
3963
+ switch(param) {
3964
+ case AL_SAMPLE_OFFSET: return (float) (offset / framesize); break;
3965
+ case AL_SEC_OFFSET: return ((float) (offset / framesize)) / ((float) freq); break;
3966
+ case AL_BYTE_OFFSET: return (float) offset; break;
3967
+ default: break;
3968
+ }
3969
+
3970
+ return 0.0f;
3971
+ }
3972
+
3973
+ static void source_set_offset(ALsource *src, ALenum param, ALfloat value)
3974
+ {
3975
+ ALCcontext *ctx = get_current_context();
3976
+ if (!ctx) {
3977
+ set_al_error(ctx, AL_INVALID_OPERATION);
3978
+ return;
3979
+ }
3980
+
3981
+ if (src->type == AL_STREAMING) {
3982
+ FIXME("set_offset for streaming sources not implemented");
3983
+ return;
3984
+ }
3985
+
3986
+ const int bufflen = (int) src->buffer->len;
3987
+ const int framesize = (int) (src->buffer->channels * sizeof (float));
3988
+ const int freq = (int) src->buffer->frequency;
3989
+ int offset = -1;
3990
+
3991
+ switch (param) {
3992
+ case AL_SAMPLE_OFFSET:
3993
+ offset = value * framesize;
3994
+ break;
3995
+ case AL_SEC_OFFSET:
3996
+ offset = value * freq * framesize;
3997
+ break;
3998
+ case AL_BYTE_OFFSET:
3999
+ offset = ((int)value / framesize) * framesize;
4000
+ break;
4001
+ }
4002
+
4003
+ if ((offset < 0) || (offset > bufflen)) {
4004
+ set_al_error(ctx, AL_INVALID_VALUE);
4005
+ return;
4006
+ }
4007
+
4008
+ /* make sure the offset lands on a sample frame boundary. */
4009
+ offset -= offset % framesize;
4010
+
4011
+ if (!SDL_AtomicGet(&src->mixer_accessible)) {
4012
+ src->offset = offset;
4013
+ } else {
4014
+ SDL_LockMutex(ctx->source_lock);
4015
+ src->offset = offset;
4016
+ SDL_UnlockMutex(ctx->source_lock);
4017
+ }
4018
+ }
4019
+
4020
+ /* deal with alSourcePlay and alSourcePlayv (etc) boiler plate... */
4021
+ #define SOURCE_STATE_TRANSITION_OP(alfn, fn) \
4022
+ void alSource##alfn(ALuint name) { source_##fn(get_current_context(), name); } \
4023
+ void alSource##alfn##v(ALsizei n, const ALuint *sources) { \
4024
+ ALCcontext *ctx = get_current_context(); \
4025
+ if (!ctx) { \
4026
+ set_al_error(ctx, AL_INVALID_OPERATION); \
4027
+ } else { \
4028
+ ALsizei i; \
4029
+ if (n > 1) { \
4030
+ FIXME("Can we do this without a full device lock?"); \
4031
+ SDL_LockAudioDevice(ctx->device->sdldevice); /* lock the SDL device so these all start mixing in the same callback. */ \
4032
+ for (i = 0; i < n; i++) { \
4033
+ source_##fn(ctx, sources[i]); \
4034
+ } \
4035
+ SDL_UnlockAudioDevice(ctx->device->sdldevice); \
4036
+ } else if (n == 1) { \
4037
+ source_##fn(ctx, *sources); \
4038
+ } \
4039
+ } \
4040
+ }
4041
+
4042
+ SOURCE_STATE_TRANSITION_OP(Stop, stop)
4043
+ SOURCE_STATE_TRANSITION_OP(Rewind, rewind)
4044
+ SOURCE_STATE_TRANSITION_OP(Pause, pause)
4045
+
4046
+
4047
+ static void _alSourceQueueBuffers(const ALuint name, const ALsizei nb, const ALuint *bufnames)
4048
+ {
4049
+ BufferQueueItem *queue = NULL;
4050
+ BufferQueueItem *queueend = NULL;
4051
+ void *ptr;
4052
+ ALsizei i;
4053
+ ALCcontext *ctx = get_current_context();
4054
+ ALsource *src = get_source(ctx, name, NULL);
4055
+ ALint queue_channels = 0;
4056
+ ALsizei queue_frequency = 0;
4057
+ ALboolean failed = AL_FALSE;
4058
+ SDL_AudioStream *stream = NULL;
4059
+
4060
+ if (!src) {
4061
+ return;
4062
+ }
4063
+
4064
+ if (src->type == AL_STATIC) {
4065
+ set_al_error(ctx, AL_INVALID_OPERATION);
4066
+ return;
4067
+ }
4068
+
4069
+ if (nb == 0) {
4070
+ return; /* nothing to do. */
4071
+ }
4072
+
4073
+ for (i = nb; i > 0; i--) { /* build list in reverse */
4074
+ BufferQueueItem *item = NULL;
4075
+ const ALuint bufname = bufnames[i-1];
4076
+ ALbuffer *buffer = bufname ? get_buffer(ctx, bufname, NULL) : NULL;
4077
+ if (!buffer && bufname) { /* uhoh, bad buffer name! */
4078
+ set_al_error(ctx, AL_INVALID_VALUE);
4079
+ failed = AL_TRUE;
4080
+ break;
4081
+ }
4082
+
4083
+ if (buffer) {
4084
+ if (queue_channels == 0) {
4085
+ SDL_assert(queue_frequency == 0);
4086
+ queue_channels = buffer->channels;
4087
+ queue_frequency = buffer->frequency;
4088
+ } else if ((queue_channels != buffer->channels) || (queue_frequency != buffer->frequency)) {
4089
+ /* the whole queue must be the same format. */
4090
+ set_al_error(ctx, AL_INVALID_VALUE);
4091
+ failed = AL_TRUE;
4092
+ break;
4093
+ }
4094
+ }
4095
+
4096
+ item = ctx->device->playback.buffer_queue_pool;
4097
+ if (item) {
4098
+ ctx->device->playback.buffer_queue_pool = item->next;
4099
+ } else { /* allocate a new item */
4100
+ item = (BufferQueueItem *) SDL_calloc(1, sizeof (BufferQueueItem));
4101
+ if (!item) {
4102
+ set_al_error(ctx, AL_OUT_OF_MEMORY);
4103
+ failed = AL_TRUE;
4104
+ break;
4105
+ }
4106
+ }
4107
+
4108
+ if (buffer) {
4109
+ SDL_AtomicIncRef(&buffer->refcount); /* mark it as in-use. */
4110
+ }
4111
+ item->buffer = buffer;
4112
+
4113
+ SDL_assert((queue != NULL) == (queueend != NULL));
4114
+ if (queueend) {
4115
+ queueend->next = item;
4116
+ } else {
4117
+ queue = item;
4118
+ }
4119
+ queueend = item;
4120
+ }
4121
+
4122
+ if (!failed) {
4123
+ if (src->queue_frequency && queue_frequency) { /* could be zero if we only queued AL name 0. */
4124
+ SDL_assert(src->queue_channels);
4125
+ SDL_assert(queue_channels);
4126
+ if ((src->queue_channels != queue_channels) || (src->queue_frequency != queue_frequency)) {
4127
+ set_al_error(ctx, AL_INVALID_VALUE);
4128
+ failed = AL_TRUE;
4129
+ }
4130
+ }
4131
+ }
4132
+
4133
+ if (!src->queue_frequency) {
4134
+ SDL_assert(!src->queue_channels);
4135
+ SDL_assert(!src->stream);
4136
+ /* We only use the stream for resampling, not for channel conversion. */
4137
+ if (ctx->device->frequency != queue_frequency) {
4138
+ stream = SDL_NewAudioStream(AUDIO_F32SYS, queue_channels, queue_frequency, AUDIO_F32SYS, queue_channels, ctx->device->frequency);
4139
+ if (!stream) {
4140
+ set_al_error(ctx, AL_OUT_OF_MEMORY);
4141
+ failed = AL_TRUE;
4142
+ }
4143
+ FIXME("need a way to prealloc space in the stream, so the mixer doesn't have to malloc");
4144
+ }
4145
+ }
4146
+
4147
+ if (failed) {
4148
+ if (queue) {
4149
+ /* Drop our claim on any buffers we planned to queue. */
4150
+ BufferQueueItem *item;
4151
+ for (item = queue; item != NULL; item = item->next) {
4152
+ if (item->buffer) {
4153
+ (void) SDL_AtomicDecRef(&item->buffer->refcount);
4154
+ }
4155
+ }
4156
+
4157
+ /* put the whole new queue back in the pool for reuse later. */
4158
+ queueend->next = ctx->device->playback.buffer_queue_pool;
4159
+ ctx->device->playback.buffer_queue_pool = queue;
4160
+ }
4161
+ if (stream) {
4162
+ SDL_FreeAudioStream(stream);
4163
+ }
4164
+ return;
4165
+ }
4166
+
4167
+ FIXME("this needs to be set way sooner");
4168
+
4169
+ FIXME("this used to have a source lock, think this one through");
4170
+ src->type = AL_STREAMING;
4171
+
4172
+ if (!src->queue_channels) {
4173
+ src->queue_channels = queue_channels;
4174
+ src->queue_frequency = queue_frequency;
4175
+ src->stream = stream;
4176
+ }
4177
+
4178
+ /* so we're going to put these on a linked list called just_queued,
4179
+ where things build up in reverse order, to keep this on a single
4180
+ pointer. The theory is we'll atomicgetptr the pointer, set that
4181
+ pointer as the "next" for our list, and then atomiccasptr our new
4182
+ list against the original pointer. If the CAS succeeds, we have
4183
+ a complete list, atomically set. If it fails, try again with
4184
+ the new pointer we found, updating our next pointer again. If it
4185
+ failed, it's because the pointer became NULL when the mixer thread
4186
+ grabbed the existing list.
4187
+
4188
+ The mixer does an atomiccasptr to grab the current list, swapping
4189
+ in a NULL. Once it has the list, it's safe to do what it likes
4190
+ with it, as nothing else owns the pointers in that list. */
4191
+
4192
+ do {
4193
+ ptr = SDL_AtomicGetPtr(&src->buffer_queue.just_queued);
4194
+ SDL_AtomicSetPtr(&queueend->next, ptr);
4195
+ } while (!SDL_AtomicCASPtr(&src->buffer_queue.just_queued, ptr, queue));
4196
+
4197
+ SDL_AtomicAdd(&src->buffer_queue.num_items, (int) nb);
4198
+ }
4199
+ ENTRYPOINTVOID(alSourceQueueBuffers,(ALuint name, ALsizei nb, const ALuint *bufnames),(name,nb,bufnames))
4200
+
4201
+ static void _alSourceUnqueueBuffers(const ALuint name, const ALsizei nb, ALuint *bufnames)
4202
+ {
4203
+ BufferQueueItem *queueend = NULL;
4204
+ BufferQueueItem *queue;
4205
+ BufferQueueItem *item;
4206
+ ALsizei i;
4207
+ ALCcontext *ctx = get_current_context();
4208
+ ALsource *src = get_source(ctx, name, NULL);
4209
+ if (!src) {
4210
+ return;
4211
+ }
4212
+
4213
+ if (src->type == AL_STATIC) {
4214
+ set_al_error(ctx, AL_INVALID_OPERATION);
4215
+ return;
4216
+ }
4217
+
4218
+ if (nb == 0) {
4219
+ return; /* nothing to do. */
4220
+ }
4221
+
4222
+ if (((ALsizei) SDL_AtomicGet(&src->buffer_queue_processed.num_items)) < nb) {
4223
+ set_al_error(ctx, AL_INVALID_VALUE);
4224
+ return;
4225
+ }
4226
+
4227
+ SDL_AtomicAdd(&src->buffer_queue_processed.num_items, -((int) nb));
4228
+
4229
+ obtain_newly_queued_buffers(&src->buffer_queue_processed);
4230
+
4231
+ item = queue = src->buffer_queue_processed.head;
4232
+ for (i = 0; i < nb; i++) {
4233
+ /* buffer_queue_processed.num_items said list was long enough. */
4234
+ SDL_assert(item != NULL);
4235
+ item = item->next;
4236
+ }
4237
+ src->buffer_queue_processed.head = item;
4238
+ if (!item) {
4239
+ src->buffer_queue_processed.tail = NULL;
4240
+ }
4241
+
4242
+ item = queue;
4243
+ for (i = 0; i < nb; i++) {
4244
+ if (item->buffer) {
4245
+ (void) SDL_AtomicDecRef(&item->buffer->refcount);
4246
+ }
4247
+ bufnames[i] = item->buffer ? item->buffer->name : 0;
4248
+ queueend = item;
4249
+ item = item->next;
4250
+ }
4251
+
4252
+ /* put the whole new queue back in the pool for reuse later. */
4253
+ SDL_assert(queueend != NULL);
4254
+ queueend->next = ctx->device->playback.buffer_queue_pool;
4255
+ ctx->device->playback.buffer_queue_pool = queue;
4256
+ }
4257
+ ENTRYPOINTVOID(alSourceUnqueueBuffers,(ALuint name, ALsizei nb, ALuint *bufnames),(name,nb,bufnames))
4258
+
4259
+ /* !!! FIXME: buffers and sources use almost identical code for blocks */
4260
+ static void _alGenBuffers(const ALsizei n, ALuint *names)
4261
+ {
4262
+ ALCcontext *ctx = get_current_context();
4263
+ ALboolean out_of_memory = AL_FALSE;
4264
+ ALsizei totalblocks;
4265
+ ALbuffer *stackobjs[16];
4266
+ ALbuffer **objects = stackobjs;
4267
+ ALsizei found = 0;
4268
+ ALsizei block_offset = 0;
4269
+ ALsizei blocki;
4270
+ ALsizei i;
4271
+
4272
+ if (!ctx) {
4273
+ set_al_error(ctx, AL_INVALID_OPERATION);
4274
+ return;
4275
+ }
4276
+
4277
+ if (n <= SDL_arraysize(stackobjs)) {
4278
+ SDL_memset(stackobjs, '\0', sizeof (ALbuffer *) * n);
4279
+ } else {
4280
+ objects = (ALbuffer **) SDL_calloc(n, sizeof (ALbuffer *));
4281
+ if (!objects) {
4282
+ set_al_error(ctx, AL_OUT_OF_MEMORY);
4283
+ return;
4284
+ }
4285
+ }
4286
+
4287
+ totalblocks = ctx->device->playback.num_buffer_blocks;
4288
+ for (blocki = 0; blocki < totalblocks; blocki++) {
4289
+ BufferBlock *block = ctx->device->playback.buffer_blocks[blocki];
4290
+ block->tmp = 0;
4291
+ if (block->used < SDL_arraysize(block->buffers)) { /* skip if full */
4292
+ for (i = 0; i < SDL_arraysize(block->buffers); i++) {
4293
+ if (!block->buffers[i].allocated) {
4294
+ block->tmp++;
4295
+ objects[found] = &block->buffers[i];
4296
+ names[found++] = (i + block_offset) + 1; /* +1 so it isn't zero. */
4297
+ if (found == n) {
4298
+ break;
4299
+ }
4300
+ }
4301
+ }
4302
+
4303
+ if (found == n) {
4304
+ break;
4305
+ }
4306
+ }
4307
+
4308
+ block_offset += SDL_arraysize(block->buffers);
4309
+ }
4310
+
4311
+ while (found < n) { /* out of blocks? Add new ones. */
4312
+ /* ctx->buffer_blocks is only accessed on the API thread under a mutex, so it's safe to realloc. */
4313
+ void *ptr = SDL_realloc(ctx->device->playback.buffer_blocks, sizeof (BufferBlock *) * (totalblocks + 1));
4314
+ BufferBlock *block;
4315
+
4316
+ if (!ptr) {
4317
+ out_of_memory = AL_TRUE;
4318
+ break;
4319
+ }
4320
+ ctx->device->playback.buffer_blocks = (BufferBlock **) ptr;
4321
+
4322
+ block = (BufferBlock *) SDL_calloc(1, sizeof (BufferBlock));
4323
+ if (!block) {
4324
+ out_of_memory = AL_TRUE;
4325
+ break;
4326
+ }
4327
+ ctx->device->playback.buffer_blocks[totalblocks] = block;
4328
+ totalblocks++;
4329
+ ctx->device->playback.num_buffer_blocks++;
4330
+
4331
+ for (i = 0; i < SDL_arraysize(block->buffers); i++) {
4332
+ block->tmp++;
4333
+ objects[found] = &block->buffers[i];
4334
+ names[found++] = (i + block_offset) + 1; /* +1 so it isn't zero. */
4335
+ if (found == n) {
4336
+ break;
4337
+ }
4338
+ }
4339
+ block_offset += SDL_arraysize(block->buffers);
4340
+ }
4341
+
4342
+ if (out_of_memory) {
4343
+ if (objects != stackobjs) SDL_free(objects);
4344
+ SDL_memset(names, '\0', sizeof (*names) * n);
4345
+ set_al_error(ctx, AL_OUT_OF_MEMORY);
4346
+ return;
4347
+ }
4348
+
4349
+ SDL_assert(found == n); /* we should have either gotten space or bailed on alloc failure */
4350
+
4351
+ /* update the "used" field in blocks with items we are taking now. */
4352
+ found = 0;
4353
+ for (blocki = 0; found < n; blocki++) {
4354
+ BufferBlock *block = ctx->device->playback.buffer_blocks[blocki];
4355
+ SDL_assert(blocki < totalblocks);
4356
+ const int foundhere = block->tmp;
4357
+ if (foundhere) {
4358
+ block->used += foundhere;
4359
+ found += foundhere;
4360
+ block->tmp = 0;
4361
+ }
4362
+ }
4363
+
4364
+ SDL_assert(found == n);
4365
+
4366
+ for (i = 0; i < n; i++) {
4367
+ ALbuffer *buffer = objects[i];
4368
+ /*printf("Generated buffer %u\n", (unsigned int) names[i]);*/
4369
+ SDL_assert(!buffer->allocated);
4370
+ SDL_zerop(buffer);
4371
+ buffer->name = names[i];
4372
+ buffer->channels = 1;
4373
+ buffer->bits = 16;
4374
+ buffer->allocated = AL_TRUE; /* we officially own it. */
4375
+ }
4376
+
4377
+ if (objects != stackobjs) SDL_free(objects);
4378
+ }
4379
+ ENTRYPOINTVOID(alGenBuffers,(ALsizei n, ALuint *names),(n,names))
4380
+
4381
+ static void _alDeleteBuffers(const ALsizei n, const ALuint *names)
4382
+ {
4383
+ ALCcontext *ctx = get_current_context();
4384
+ ALsizei i;
4385
+
4386
+ if (!ctx) {
4387
+ set_al_error(ctx, AL_INVALID_OPERATION);
4388
+ return;
4389
+ }
4390
+
4391
+ for (i = 0; i < n; i++) {
4392
+ const ALuint name = names[i];
4393
+ if (name == 0) {
4394
+ /* ignore it. */
4395
+ } else {
4396
+ ALbuffer *buffer = get_buffer(ctx, name, NULL);
4397
+ if (!buffer) {
4398
+ /* "If one or more of the specified names is not valid, an AL_INVALID_NAME error will be recorded, and no objects will be deleted." */
4399
+ set_al_error(ctx, AL_INVALID_NAME);
4400
+ return;
4401
+ } else if (SDL_AtomicGet(&buffer->refcount) != 0) {
4402
+ set_al_error(ctx, AL_INVALID_OPERATION); /* still in use */
4403
+ return;
4404
+ }
4405
+ }
4406
+ }
4407
+
4408
+ for (i = 0; i < n; i++) {
4409
+ const ALuint name = names[i];
4410
+ if (name != 0) {
4411
+ BufferBlock *block;
4412
+ ALbuffer *buffer = get_buffer(ctx, name, &block);
4413
+ void *data;
4414
+ SDL_assert(buffer != NULL);
4415
+ data = (void *) buffer->data;
4416
+ buffer->allocated = AL_FALSE;
4417
+ buffer->data = NULL;
4418
+ free_simd_aligned(data);
4419
+ block->used--;
4420
+ }
4421
+ }
4422
+ }
4423
+ ENTRYPOINTVOID(alDeleteBuffers,(ALsizei n, const ALuint *names),(n,names))
4424
+
4425
+ static ALboolean _alIsBuffer(ALuint name)
4426
+ {
4427
+ ALCcontext *ctx = get_current_context();
4428
+ return (ctx && (get_buffer(ctx, name, NULL) != NULL)) ? AL_TRUE : AL_FALSE;
4429
+ }
4430
+ ENTRYPOINT(ALboolean,alIsBuffer,(ALuint name),(name))
4431
+
4432
+ static void _alBufferData(const ALuint name, const ALenum alfmt, const ALvoid *data, const ALsizei size, const ALsizei freq)
4433
+ {
4434
+ ALCcontext *ctx = get_current_context();
4435
+ ALbuffer *buffer = get_buffer(ctx, name, NULL);
4436
+ SDL_AudioCVT sdlcvt;
4437
+ Uint8 channels;
4438
+ SDL_AudioFormat sdlfmt;
4439
+ ALCsizei framesize;
4440
+ int rc;
4441
+ int prevrefcount;
4442
+
4443
+ if (!buffer) return;
4444
+
4445
+ if (!alcfmt_to_sdlfmt(alfmt, &sdlfmt, &channels, &framesize)) {
4446
+ set_al_error(ctx, AL_INVALID_VALUE);
4447
+ return;
4448
+ }
4449
+
4450
+ /* increment refcount so this can't be deleted or alBufferData'd from another thread */
4451
+ prevrefcount = SDL_AtomicIncRef(&buffer->refcount);
4452
+ SDL_assert(prevrefcount >= 0);
4453
+ if (prevrefcount != 0) {
4454
+ /* this buffer is being used by some source. Unqueue it first. */
4455
+ (void) SDL_AtomicDecRef(&buffer->refcount);
4456
+ set_al_error(ctx, AL_INVALID_OPERATION);
4457
+ return;
4458
+ }
4459
+
4460
+ /* This check was from the wild west of lock-free programming, now we shouldn't pass get_buffer() if not allocated. */
4461
+ SDL_assert(buffer->allocated);
4462
+
4463
+ /* right now we take a moment to convert the data to float32, since that's
4464
+ the format we want to work in, but we don't resample or change the channels */
4465
+ SDL_zero(sdlcvt);
4466
+ rc = SDL_BuildAudioCVT(&sdlcvt, sdlfmt, channels, (int) freq, AUDIO_F32SYS, channels, (int) freq);
4467
+ if (rc == -1) {
4468
+ (void) SDL_AtomicDecRef(&buffer->refcount);
4469
+ set_al_error(ctx, AL_OUT_OF_MEMORY); /* not really, but oh well. */
4470
+ return;
4471
+ }
4472
+
4473
+ sdlcvt.len = sdlcvt.len_cvt = size;
4474
+ sdlcvt.buf = (Uint8 *) calloc_simd_aligned(size * sdlcvt.len_mult);
4475
+ if (!sdlcvt.buf) {
4476
+ (void) SDL_AtomicDecRef(&buffer->refcount);
4477
+ set_al_error(ctx, AL_OUT_OF_MEMORY);
4478
+ return;
4479
+ }
4480
+ SDL_memcpy(sdlcvt.buf, data, size);
4481
+
4482
+ if (rc == 1) { /* conversion necessary */
4483
+ rc = SDL_ConvertAudio(&sdlcvt);
4484
+ SDL_assert(rc == 0); /* this shouldn't fail. */
4485
+ if (sdlcvt.len_cvt < (size * sdlcvt.len_mult)) { /* maybe shrink buffer */
4486
+ void *ptr = SDL_realloc(sdlcvt.buf, sdlcvt.len_cvt);
4487
+ if (ptr) {
4488
+ sdlcvt.buf = (Uint8 *) ptr;
4489
+ }
4490
+ }
4491
+ }
4492
+
4493
+ free_simd_aligned((void *) buffer->data); /* nuke any previous data. */
4494
+ buffer->data = (const float *) sdlcvt.buf;
4495
+ buffer->channels = (ALint) channels;
4496
+ buffer->bits = (ALint) SDL_AUDIO_BITSIZE(sdlfmt); /* we're in float32, though. */
4497
+ buffer->frequency = freq;
4498
+ buffer->len = (ALsizei) sdlcvt.len_cvt;
4499
+ (void) SDL_AtomicDecRef(&buffer->refcount); /* ready to go! */
4500
+ }
4501
+ ENTRYPOINTVOID(alBufferData,(ALuint name, ALenum alfmt, const ALvoid *data, ALsizei size, ALsizei freq),(name,alfmt,data,size,freq))
4502
+
4503
+ static void _alBufferfv(const ALuint name, const ALenum param, const ALfloat *values)
4504
+ {
4505
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in core OpenAL 1.1 uses this */
4506
+ }
4507
+ ENTRYPOINTVOID(alBufferfv,(ALuint name, ALenum param, const ALfloat *values),(name,param,values))
4508
+
4509
+ static void _alBufferf(const ALuint name, const ALenum param, const ALfloat value)
4510
+ {
4511
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in core OpenAL 1.1 uses this */
4512
+ }
4513
+ ENTRYPOINTVOID(alBufferf,(ALuint name, ALenum param, ALfloat value),(name,param,value))
4514
+
4515
+ static void _alBuffer3f(const ALuint name, const ALenum param, const ALfloat value1, const ALfloat value2, const ALfloat value3)
4516
+ {
4517
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in core OpenAL 1.1 uses this */
4518
+ }
4519
+ ENTRYPOINTVOID(alBuffer3f,(ALuint name, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3),(name,param,value1,value2,value3))
4520
+
4521
+ static void _alBufferiv(const ALuint name, const ALenum param, const ALint *values)
4522
+ {
4523
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in core OpenAL 1.1 uses this */
4524
+ }
4525
+ ENTRYPOINTVOID(alBufferiv,(ALuint name, ALenum param, const ALint *values),(name,param,values))
4526
+
4527
+ static void _alBufferi(const ALuint name, const ALenum param, const ALint value)
4528
+ {
4529
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in core OpenAL 1.1 uses this */
4530
+ }
4531
+ ENTRYPOINTVOID(alBufferi,(ALuint name, ALenum param, ALint value),(name,param,value))
4532
+
4533
+ static void _alBuffer3i(const ALuint name, const ALenum param, const ALint value1, const ALint value2, const ALint value3)
4534
+ {
4535
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in core OpenAL 1.1 uses this */
4536
+ }
4537
+ ENTRYPOINTVOID(alBuffer3i,(ALuint name, ALenum param, ALint value1, ALint value2, ALint value3),(name,param,value1,value2,value3))
4538
+
4539
+ static void _alGetBufferfv(const ALuint name, const ALenum param, const ALfloat *values)
4540
+ {
4541
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in core OpenAL 1.1 uses this */
4542
+ }
4543
+ ENTRYPOINTVOID(alGetBufferfv,(ALuint name, ALenum param, ALfloat *values),(name,param,values))
4544
+
4545
+ static void _alGetBufferf(const ALuint name, const ALenum param, ALfloat *value)
4546
+ {
4547
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in core OpenAL 1.1 uses this */
4548
+ }
4549
+ ENTRYPOINTVOID(alGetBufferf,(ALuint name, ALenum param, ALfloat *value),(name,param,value))
4550
+
4551
+ static void _alGetBuffer3f(const ALuint name, const ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
4552
+ {
4553
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in core OpenAL 1.1 uses this */
4554
+ }
4555
+ ENTRYPOINTVOID(alGetBuffer3f,(ALuint name, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3),(name,param,value1,value2,value3))
4556
+
4557
+ static void _alGetBufferi(const ALuint name, const ALenum param, ALint *value)
4558
+ {
4559
+ switch (param) {
4560
+ case AL_FREQUENCY:
4561
+ case AL_SIZE:
4562
+ case AL_BITS:
4563
+ case AL_CHANNELS:
4564
+ alGetBufferiv(name, param, value);
4565
+ break;
4566
+ default: set_al_error(get_current_context(), AL_INVALID_ENUM); break;
4567
+ }
4568
+ }
4569
+ ENTRYPOINTVOID(alGetBufferi,(ALuint name, ALenum param, ALint *value),(name,param,value))
4570
+
4571
+ static void _alGetBuffer3i(const ALuint name, const ALenum param, ALint *value1, ALint *value2, ALint *value3)
4572
+ {
4573
+ set_al_error(get_current_context(), AL_INVALID_ENUM); /* nothing in core OpenAL 1.1 uses this */
4574
+ }
4575
+ ENTRYPOINTVOID(alGetBuffer3i,(ALuint name, ALenum param, ALint *value1, ALint *value2, ALint *value3),(name,param,value1,value2,value3))
4576
+
4577
+ static void _alGetBufferiv(const ALuint name, const ALenum param, ALint *values)
4578
+ {
4579
+ ALCcontext *ctx = get_current_context();
4580
+ ALbuffer *buffer = get_buffer(ctx, name, NULL);
4581
+ if (!buffer) return;
4582
+
4583
+ switch (param) {
4584
+ case AL_FREQUENCY: *values = (ALint) buffer->frequency; break;
4585
+ case AL_SIZE: *values = (ALint) buffer->len; break;
4586
+ case AL_BITS: *values = (ALint) buffer->bits; break;
4587
+ case AL_CHANNELS: *values = (ALint) buffer->channels; break;
4588
+ default: set_al_error(ctx, AL_INVALID_ENUM); break;
4589
+ }
4590
+ }
4591
+ ENTRYPOINTVOID(alGetBufferiv,(ALuint name, ALenum param, ALint *values),(name,param,values))
4592
+
4593
+ /* end of mojoal.c ... */
4594
+