sfml3-rb 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +9 -0
- data/CHANGELOG.md +67 -16
- data/README.md +123 -32
- data/TODO.md +18 -11
- data/ext/audio/audio_enums.c +58 -20
- data/ext/audio/listener.c +89 -6
- data/ext/audio/music.c +212 -5
- data/ext/audio/sound.c +218 -9
- data/ext/audio/sound_buffer.c +197 -47
- data/ext/audio/sound_buffer_recorder.c +90 -18
- data/ext/audio/sound_recorder.c +188 -58
- data/ext/audio/sound_source_cone.c +127 -33
- data/ext/audio/sound_stream.c +204 -21
- data/ext/core/exceptions.c +10 -8
- data/ext/core/unicode.c +12 -2
- data/ext/ext.c +8 -0
- data/ext/graphics/blend_mode.c +218 -60
- data/ext/graphics/circle.c +300 -16
- data/ext/graphics/color.c +253 -94
- data/ext/graphics/drawable.c +16 -4
- data/ext/graphics/font.c +144 -25
- data/ext/graphics/glyph.c +38 -8
- data/ext/graphics/image.c +205 -41
- data/ext/graphics/polygon.c +317 -30
- data/ext/graphics/rect.c +225 -60
- data/ext/graphics/rectangle.c +307 -35
- data/ext/graphics/render_state.c +124 -12
- data/ext/graphics/render_texture.c +191 -14
- data/ext/graphics/render_window.c +55 -0
- data/ext/graphics/render_window.h +10 -0
- data/ext/graphics/shader.c +289 -17
- data/ext/graphics/shape.c +265 -32
- data/ext/graphics/sprite.c +245 -30
- data/ext/graphics/stencil_mode.c +163 -46
- data/ext/graphics/target.c +121 -43
- data/ext/graphics/target.h +31 -17
- data/ext/graphics/text.c +339 -19
- data/ext/graphics/texture.c +291 -6
- data/ext/graphics/transform.c +178 -5
- data/ext/graphics/transformable.c +127 -12
- data/ext/graphics/vertex.c +98 -6
- data/ext/graphics/vertex_array.c +142 -21
- data/ext/graphics/vertex_buffer.c +154 -12
- data/ext/graphics/view.c +140 -6
- data/ext/network/ftp.c +278 -12
- data/ext/network/http.c +179 -44
- data/ext/network/ip_address.c +127 -35
- data/ext/network/network_enums.c +248 -141
- data/ext/network/packet.c +280 -41
- data/ext/network/socket_selector.c +98 -21
- data/ext/network/tcp_listener.c +82 -18
- data/ext/network/tcp_socket.c +135 -27
- data/ext/network/udp_socket.c +129 -28
- data/ext/ports.rb +7 -0
- data/ext/system/buffer.c +47 -16
- data/ext/system/clock.c +77 -12
- data/ext/system/input_stream.c +113 -51
- data/ext/system/sleep.c +10 -2
- data/ext/system/time.c +182 -40
- data/ext/system/vec2.c +166 -39
- data/ext/system/vec3.c +177 -39
- data/ext/window/clipboard.c +47 -17
- data/ext/window/context.c +66 -13
- data/ext/window/context_settings.c +132 -42
- data/ext/window/cursor.c +45 -9
- data/ext/window/event.c +164 -53
- data/ext/window/joystick.c +69 -3
- data/ext/window/keyboard.c +168 -116
- data/ext/window/mouse.c +64 -13
- data/ext/window/sensor.c +28 -2
- data/ext/window/touch.c +32 -12
- data/ext/window/video_mode.c +93 -31
- data/ext/window/vulkan.c +34 -6
- data/ext/window/window.c +228 -229
- data/ext/window/window.h +4 -4
- data/ext/window/window_base.c +251 -0
- data/ext/window/window_base.h +43 -0
- data/ext/window/window_base.inc +236 -0
- data/lib/sfml/version.rb +1 -1
- data/sig/audio/audio_enums.rbs +30 -0
- data/sig/audio/listener.rbs +16 -0
- data/sig/audio/music.rbs +56 -0
- data/sig/audio/sound.rbs +51 -0
- data/sig/audio/sound_buffer.rbs +17 -0
- data/sig/audio/sound_buffer_recorder.rbs +14 -0
- data/sig/audio/sound_recorder.rbs +17 -0
- data/sig/audio/sound_source_cone.rbs +14 -0
- data/sig/audio/sound_stream.rbs +51 -0
- data/sig/graphics/blend_mode.rbs +29 -0
- data/sig/graphics/circle.rbs +44 -0
- data/sig/graphics/color.rbs +39 -0
- data/sig/graphics/drawable.rbs +5 -0
- data/sig/graphics/font.rbs +20 -0
- data/sig/graphics/glyph.rbs +7 -0
- data/sig/graphics/image.rbs +22 -0
- data/sig/graphics/polygon.rbs +42 -0
- data/sig/graphics/rect.rbs +33 -0
- data/sig/graphics/rectangle.rbs +42 -0
- data/sig/graphics/render_state.rbs +21 -0
- data/sig/graphics/render_target.rbs +18 -0
- data/sig/graphics/render_texture.rbs +39 -0
- data/sig/graphics/render_window.rbs +7 -0
- data/sig/graphics/shader.rbs +39 -0
- data/sig/graphics/shape.rbs +38 -0
- data/sig/graphics/sprite.rbs +34 -0
- data/sig/graphics/stencil_mode.rbs +18 -0
- data/sig/graphics/target.rbs +8 -0
- data/sig/graphics/text.rbs +48 -0
- data/sig/graphics/texture.rbs +34 -0
- data/sig/graphics/transform.rbs +37 -0
- data/sig/graphics/transformable.rbs +25 -0
- data/sig/graphics/vertex.rbs +17 -0
- data/sig/graphics/vertex_array.rbs +17 -0
- data/sig/graphics/vertex_buffer.rbs +22 -0
- data/sig/graphics/view.rbs +23 -0
- data/sig/network/ftp.rbs +46 -0
- data/sig/network/http.rbs +27 -0
- data/sig/network/ip_address.rbs +22 -0
- data/sig/network/network_enums.rbs +95 -0
- data/sig/network/packet.rbs +40 -0
- data/sig/network/socket_selector.rbs +14 -0
- data/sig/network/tcp_listener.rbs +13 -0
- data/sig/network/tcp_socket.rbs +18 -0
- data/sig/network/udp_socket.rbs +17 -0
- data/sig/sfml/version.rbs +3 -0
- data/sig/system/buffer.rbs +11 -0
- data/sig/system/clock.rbs +13 -0
- data/sig/system/input_stream.rbs +7 -0
- data/sig/system/sleep.rbs +3 -0
- data/sig/system/time.rbs +28 -0
- data/sig/system/vec2.rbs +26 -0
- data/sig/system/vec3.rbs +27 -0
- data/sig/window/clipboard.rbs +8 -0
- data/sig/window/context.rbs +11 -0
- data/sig/window/context_settings.rbs +20 -0
- data/sig/window/cursor.rbs +6 -0
- data/sig/window/event.rbs +20 -0
- data/sig/window/joystick.rbs +15 -0
- data/sig/window/keyboard.rbs +10 -0
- data/sig/window/mouse.rbs +9 -0
- data/sig/window/sensor.rbs +8 -0
- data/sig/window/touch.rbs +6 -0
- data/sig/window/video_mode.rbs +16 -0
- data/sig/window/vulkan.rbs +7 -0
- data/sig/window/window.rbs +34 -0
- data/sig/window/window_base.rbs +33 -0
- metadata +76 -3
data/ext/audio/sound.c
CHANGED
|
@@ -33,15 +33,29 @@ static void* Sound_destroy_without_gvl(void* handle) {
|
|
|
33
33
|
static void Sound_free(void* ptr) {
|
|
34
34
|
Sound* sound = ptr;
|
|
35
35
|
|
|
36
|
+
/* Destroy (which blocks until CSFML guarantees no in-flight audio-thread
|
|
37
|
+
callback still references this source) before releasing the effect
|
|
38
|
+
slot, not after: releasing first opens a window where another Ruby
|
|
39
|
+
thread's effect_processor_acquire could reuse the slot number for an
|
|
40
|
+
unrelated new source while this source's callback might still be
|
|
41
|
+
executing against it. */
|
|
42
|
+
if (sound->source.handle != NULL) {
|
|
43
|
+
rb_thread_call_without_gvl(Sound_destroy_without_gvl, sound->source.handle, RUBY_UBF_IO,
|
|
44
|
+
NULL);
|
|
45
|
+
}
|
|
46
|
+
|
|
36
47
|
effect_processor_release(sound->source.effect_slot);
|
|
37
|
-
rb_thread_call_without_gvl(Sound_destroy_without_gvl, sound->source.handle, RUBY_UBF_IO, NULL);
|
|
38
48
|
free(sound);
|
|
39
49
|
}
|
|
40
50
|
|
|
51
|
+
/* Deliberately no RUBY_TYPED_FREE_IMMEDIATELY: Sound_free releases the GVL for
|
|
52
|
+
sfSound_destroy, and freeing during GC (which the flag requests) would let
|
|
53
|
+
another thread allocate while GC is mid-cycle, which Ruby aborts on as
|
|
54
|
+
"object allocation during garbage collection phase". Deferred finalization
|
|
55
|
+
runs the same free with the GVL held and no GC in progress. */
|
|
41
56
|
static const rb_data_type_t Sound_data_type = {
|
|
42
57
|
.wrap_struct_name = "SFML::Sound",
|
|
43
|
-
.function = {.dmark = Sound_mark, .dfree = Sound_free, .dsize = NULL}
|
|
44
|
-
.flags = RUBY_TYPED_FREE_IMMEDIATELY};
|
|
58
|
+
.function = {.dmark = Sound_mark, .dfree = Sound_free, .dsize = NULL}};
|
|
45
59
|
|
|
46
60
|
static VALUE Sound_wrap(VALUE klass, sfSound* handle, VALUE rb_buffer) {
|
|
47
61
|
Sound* ptr;
|
|
@@ -58,14 +72,61 @@ static VALUE Sound_wrap(VALUE klass, sfSound* handle, VALUE rb_buffer) {
|
|
|
58
72
|
return TypedData_Wrap_Struct(klass, &Sound_data_type, ptr);
|
|
59
73
|
}
|
|
60
74
|
|
|
61
|
-
static VALUE
|
|
75
|
+
static VALUE Sound_initialize_copy(VALUE self, VALUE other) {
|
|
76
|
+
(void)other;
|
|
77
|
+
rb_raise(rb_eTypeError, "can't copy a %s", rb_obj_classname(self));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static VALUE Sound_alloc(VALUE klass) {
|
|
81
|
+
Sound* ptr = malloc(sizeof(Sound));
|
|
82
|
+
|
|
83
|
+
if (ptr == NULL) {
|
|
84
|
+
rb_raise(rb_eNoMemError, "failed to allocate sound");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
ptr->source.handle = NULL;
|
|
88
|
+
ptr->source.effect_slot = -1;
|
|
89
|
+
ptr->rb_buffer = Qnil;
|
|
90
|
+
|
|
91
|
+
return TypedData_Wrap_Struct(klass, &Sound_data_type, ptr);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* call-seq:
|
|
95
|
+
* Sound.new(buffer) -> Sound
|
|
96
|
+
*
|
|
97
|
+
* Creates a sound that plays the given buffer.
|
|
98
|
+
*
|
|
99
|
+
* @return [Sound]
|
|
100
|
+
* @raise [ArgumentError] if +buffer+ is not a SoundBuffer
|
|
101
|
+
*/
|
|
102
|
+
static VALUE Sound_initialize(VALUE self, VALUE rb_buffer) {
|
|
103
|
+
Sound* sound;
|
|
104
|
+
sfSound* handle;
|
|
105
|
+
|
|
62
106
|
if (!rb_obj_is_kind_of(rb_buffer, Get_Klass_SoundBuffer())) {
|
|
63
107
|
raise_invalid_argument_class(Get_Klass_SoundBuffer());
|
|
64
108
|
}
|
|
65
109
|
|
|
66
|
-
|
|
110
|
+
sound = (Sound*)Get_Sound_Struct(self);
|
|
111
|
+
|
|
112
|
+
handle = sfSound_create(Get_SoundBuffer_Struct(rb_buffer));
|
|
113
|
+
|
|
114
|
+
if (handle == NULL) {
|
|
115
|
+
rb_raise(rb_eRuntimeError, "failed to create sound");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
sound->source.handle = handle;
|
|
119
|
+
sound->rb_buffer = rb_buffer;
|
|
120
|
+
|
|
121
|
+
return self;
|
|
67
122
|
}
|
|
68
123
|
|
|
124
|
+
/* call-seq: copy -> Sound
|
|
125
|
+
*
|
|
126
|
+
* Creates an independent copy of the sound that shares its buffer.
|
|
127
|
+
*
|
|
128
|
+
* @return [Sound] an independent copy that shares the same SoundBuffer
|
|
129
|
+
*/
|
|
69
130
|
static VALUE Sound_copy(VALUE self) {
|
|
70
131
|
Sound* sound = (Sound*)Get_Sound_Struct(self);
|
|
71
132
|
|
|
@@ -73,6 +134,12 @@ static VALUE Sound_copy(VALUE self) {
|
|
|
73
134
|
sound->rb_buffer);
|
|
74
135
|
}
|
|
75
136
|
|
|
137
|
+
/* call-seq: buffer -> SoundBuffer
|
|
138
|
+
*
|
|
139
|
+
* Returns the buffer currently attached to this sound.
|
|
140
|
+
*
|
|
141
|
+
* @return [SoundBuffer] the buffer currently attached to this sound
|
|
142
|
+
*/
|
|
76
143
|
static VALUE Sound_get_buffer(VALUE self) {
|
|
77
144
|
return ((Sound*)Get_Sound_Struct(self))->rb_buffer;
|
|
78
145
|
}
|
|
@@ -93,6 +160,16 @@ static void* Sound_set_buffer_without_gvl(void* raw) {
|
|
|
93
160
|
return NULL;
|
|
94
161
|
}
|
|
95
162
|
|
|
163
|
+
/* call-seq:
|
|
164
|
+
* buffer=(value) -> SoundBuffer
|
|
165
|
+
*
|
|
166
|
+
* Attaches a new SoundBuffer. If a buffer is already attached and the sound
|
|
167
|
+
* is playing, it is stopped first, which may briefly block the calling
|
|
168
|
+
* thread (see #stop).
|
|
169
|
+
*
|
|
170
|
+
* @return [SoundBuffer] +value+
|
|
171
|
+
* @raise [ArgumentError] if +value+ is not a SoundBuffer
|
|
172
|
+
*/
|
|
96
173
|
static VALUE Sound_set_buffer(VALUE self, VALUE rb_buffer) {
|
|
97
174
|
Sound* sound = (Sound*)Get_Sound_Struct(self);
|
|
98
175
|
SoundSetBufferArgs args;
|
|
@@ -116,10 +193,142 @@ static VALUE Sound_set_buffer(VALUE self, VALUE rb_buffer) {
|
|
|
116
193
|
#undef SS_FN
|
|
117
194
|
#undef SS_METHOD
|
|
118
195
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
196
|
+
/* Document-class: SFML::Sound
|
|
197
|
+
* A sound playing directly from a SoundBuffer held fully in memory. Suited
|
|
198
|
+
* to short effects; for long files prefer Music, which streams instead.
|
|
199
|
+
*
|
|
200
|
+
* @!method play
|
|
201
|
+
* Starts playback, or resumes it when paused.
|
|
202
|
+
* @return [self]
|
|
203
|
+
* @!method pause
|
|
204
|
+
* Pauses playback, keeping the current playing offset.
|
|
205
|
+
* @return [self]
|
|
206
|
+
* @!method stop
|
|
207
|
+
* Stops playback and rewinds to the beginning. May briefly block the
|
|
208
|
+
* calling thread if an audio-thread callback for this source is in
|
|
209
|
+
* flight.
|
|
210
|
+
* @return [self]
|
|
211
|
+
* @!method status
|
|
212
|
+
* Returns the current playback status.
|
|
213
|
+
* @return [Symbol] one of +:stopped+, +:paused+, +:playing+
|
|
214
|
+
* @!method looping?
|
|
215
|
+
* Returns +true+ if playback loops back to the start on completion.
|
|
216
|
+
* @return [Boolean]
|
|
217
|
+
* @!method looping=(value)
|
|
218
|
+
* Enables or disables looping.
|
|
219
|
+
* @return [Boolean]
|
|
220
|
+
* @!method pitch
|
|
221
|
+
* Returns the pitch scaling factor.
|
|
222
|
+
* @return [Float]
|
|
223
|
+
* @!method pitch=(value)
|
|
224
|
+
* Sets the pitch scaling factor.
|
|
225
|
+
* @return [Float]
|
|
226
|
+
* @!method pan
|
|
227
|
+
* Returns the source's stereo pan.
|
|
228
|
+
* @return [Float] stereo pan, -1 (left) to 1 (right)
|
|
229
|
+
* @!method pan=(value)
|
|
230
|
+
* Sets the source's stereo pan.
|
|
231
|
+
* @return [Float]
|
|
232
|
+
* @!method volume
|
|
233
|
+
* Returns the source's volume.
|
|
234
|
+
* @return [Float] 0 to 100
|
|
235
|
+
* @!method volume=(value)
|
|
236
|
+
* Sets the source's volume.
|
|
237
|
+
* @return [Float]
|
|
238
|
+
* @!method spatialization_enabled?
|
|
239
|
+
* Returns +true+ if 3D spatialization is enabled.
|
|
240
|
+
* @return [Boolean]
|
|
241
|
+
* @!method spatialization_enabled=(value)
|
|
242
|
+
* Enables or disables 3D spatialization.
|
|
243
|
+
* @return [Boolean]
|
|
244
|
+
* @!method position
|
|
245
|
+
* Returns the source's position in 3D space.
|
|
246
|
+
* @return [Vector3]
|
|
247
|
+
* @!method position=(value)
|
|
248
|
+
* Sets the source's position in 3D space.
|
|
249
|
+
* @return [Vector3]
|
|
250
|
+
* @!method direction
|
|
251
|
+
* Returns the direction the source is facing.
|
|
252
|
+
* @return [Vector3]
|
|
253
|
+
* @!method direction=(value)
|
|
254
|
+
* Sets the direction the source is facing.
|
|
255
|
+
* @return [Vector3]
|
|
256
|
+
* @!method velocity
|
|
257
|
+
* Returns the source's velocity, used for Doppler calculations.
|
|
258
|
+
* @return [Vector3]
|
|
259
|
+
* @!method velocity=(value)
|
|
260
|
+
* Sets the source's velocity for Doppler calculations.
|
|
261
|
+
* @return [Vector3]
|
|
262
|
+
* @!method cone
|
|
263
|
+
* Returns the source's directional attenuation cone.
|
|
264
|
+
* @return [SoundSourceCone]
|
|
265
|
+
* @!method cone=(value)
|
|
266
|
+
* Sets the source's directional attenuation cone.
|
|
267
|
+
* @return [SoundSourceCone]
|
|
268
|
+
* @!method doppler_factor
|
|
269
|
+
* Returns the factor by which the Doppler effect is scaled.
|
|
270
|
+
* @return [Float]
|
|
271
|
+
* @!method doppler_factor=(value)
|
|
272
|
+
* Sets the factor by which the Doppler effect is scaled.
|
|
273
|
+
* @return [Float]
|
|
274
|
+
* @!method directional_attenuation_factor
|
|
275
|
+
* Returns the factor controlling directional attenuation.
|
|
276
|
+
* @return [Float]
|
|
277
|
+
* @!method directional_attenuation_factor=(value)
|
|
278
|
+
* Sets the factor controlling directional attenuation.
|
|
279
|
+
* @return [Float]
|
|
280
|
+
* @!method relative_to_listener?
|
|
281
|
+
* Returns +true+ if the source is positioned relative to the listener.
|
|
282
|
+
* @return [Boolean]
|
|
283
|
+
* @!method relative_to_listener=(value)
|
|
284
|
+
* Makes the source relative to, or independent of, the listener.
|
|
285
|
+
* @return [Boolean]
|
|
286
|
+
* @!method min_distance
|
|
287
|
+
* Returns the minimum distance of the distance-attenuation model.
|
|
288
|
+
* @return [Float]
|
|
289
|
+
* @!method min_distance=(value)
|
|
290
|
+
* Sets the minimum distance of the distance-attenuation model.
|
|
291
|
+
* @return [Float]
|
|
292
|
+
* @!method max_distance
|
|
293
|
+
* Returns the maximum distance of the distance-attenuation model.
|
|
294
|
+
* @return [Float]
|
|
295
|
+
* @!method max_distance=(value)
|
|
296
|
+
* Sets the maximum distance of the distance-attenuation model.
|
|
297
|
+
* @return [Float]
|
|
298
|
+
* @!method min_gain
|
|
299
|
+
* Returns the minimum gain of the distance-attenuation model.
|
|
300
|
+
* @return [Float]
|
|
301
|
+
* @!method min_gain=(value)
|
|
302
|
+
* Sets the minimum gain of the distance-attenuation model.
|
|
303
|
+
* @return [Float]
|
|
304
|
+
* @!method max_gain
|
|
305
|
+
* Returns the maximum gain of the distance-attenuation model.
|
|
306
|
+
* @return [Float]
|
|
307
|
+
* @!method max_gain=(value)
|
|
308
|
+
* Sets the maximum gain of the distance-attenuation model.
|
|
309
|
+
* @return [Float]
|
|
310
|
+
* @!method attenuation
|
|
311
|
+
* Returns the distance-attenuation factor.
|
|
312
|
+
* @return [Float]
|
|
313
|
+
* @!method attenuation=(value)
|
|
314
|
+
* Sets the distance-attenuation factor.
|
|
315
|
+
* @return [Float]
|
|
316
|
+
* @!method playing_offset
|
|
317
|
+
* Returns the current playing offset.
|
|
318
|
+
* @return [Time]
|
|
319
|
+
* @!method playing_offset=(value)
|
|
320
|
+
* Seeks to the given playing offset.
|
|
321
|
+
* @return [Time]
|
|
322
|
+
* @!method effect_processor=(proc)
|
|
323
|
+
* Installs a Proc that post-processes this source's audio in real time.
|
|
324
|
+
* @return [Proc] +proc+
|
|
325
|
+
*/
|
|
326
|
+
void Init_Sound(VALUE rb_mSFML) {
|
|
327
|
+
rb_cSound = rb_define_class_under(rb_mSFML, "Sound", rb_cObject);
|
|
328
|
+
|
|
329
|
+
rb_define_alloc_func(rb_cSound, Sound_alloc);
|
|
330
|
+
rb_define_method(rb_cSound, "initialize", Sound_initialize, 1);
|
|
331
|
+
rb_define_private_method(rb_cSound, "initialize_copy", Sound_initialize_copy, 1);
|
|
123
332
|
|
|
124
333
|
rb_define_method(rb_cSound, "copy", Sound_copy, 0);
|
|
125
334
|
rb_define_method(rb_cSound, "buffer", Sound_get_buffer, 0);
|
data/ext/audio/sound_buffer.c
CHANGED
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
#include "system/time.h"
|
|
13
13
|
|
|
14
14
|
typedef struct {
|
|
15
|
-
sfSoundBuffer
|
|
15
|
+
sfSoundBuffer* buffer;
|
|
16
16
|
bool owns;
|
|
17
17
|
} SoundBuffer;
|
|
18
18
|
|
|
19
19
|
static VALUE rb_cSoundBuffer;
|
|
20
20
|
|
|
21
|
-
static void SoundBuffer_free(void
|
|
22
|
-
SoundBuffer
|
|
21
|
+
static void SoundBuffer_free(void* ptr) {
|
|
22
|
+
SoundBuffer* sound_buffer = ptr;
|
|
23
23
|
|
|
24
24
|
if (sound_buffer->owns) {
|
|
25
25
|
sfSoundBuffer_destroy(sound_buffer->buffer);
|
|
@@ -31,11 +31,10 @@ static void SoundBuffer_free(void *ptr) {
|
|
|
31
31
|
static const rb_data_type_t SoundBuffer_data_type = {
|
|
32
32
|
.wrap_struct_name = "SFML::SoundBuffer",
|
|
33
33
|
.function = {.dmark = NULL, .dfree = SoundBuffer_free, .dsize = NULL},
|
|
34
|
-
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
35
|
-
};
|
|
34
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY};
|
|
36
35
|
|
|
37
|
-
static VALUE SoundBuffer_wrap(VALUE klass, sfSoundBuffer
|
|
38
|
-
SoundBuffer
|
|
36
|
+
static VALUE SoundBuffer_wrap(VALUE klass, sfSoundBuffer* buffer) {
|
|
37
|
+
SoundBuffer* ptr;
|
|
39
38
|
|
|
40
39
|
if (buffer == NULL) {
|
|
41
40
|
rb_raise(rb_eRuntimeError, "failed to create sound buffer");
|
|
@@ -48,36 +47,60 @@ static VALUE SoundBuffer_wrap(VALUE klass, sfSoundBuffer *buffer) {
|
|
|
48
47
|
return TypedData_Wrap_Struct(klass, &SoundBuffer_data_type, ptr);
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
VALUE sound_buffer_from_borrowed(const sfSoundBuffer
|
|
52
|
-
SoundBuffer
|
|
50
|
+
VALUE sound_buffer_from_borrowed(const sfSoundBuffer* buffer) {
|
|
51
|
+
SoundBuffer* ptr;
|
|
53
52
|
|
|
54
53
|
if (buffer == NULL) {
|
|
55
54
|
return Qnil;
|
|
56
55
|
}
|
|
57
56
|
|
|
58
57
|
ptr = malloc(sizeof(SoundBuffer));
|
|
59
|
-
ptr->buffer = (sfSoundBuffer
|
|
58
|
+
ptr->buffer = (sfSoundBuffer*)buffer;
|
|
60
59
|
ptr->owns = false;
|
|
61
60
|
|
|
62
61
|
return TypedData_Wrap_Struct(rb_cSoundBuffer, &SoundBuffer_data_type, ptr);
|
|
63
62
|
}
|
|
64
63
|
|
|
64
|
+
/* call-seq:
|
|
65
|
+
* SoundBuffer.from_file(path) -> SoundBuffer
|
|
66
|
+
*
|
|
67
|
+
* Loads and decodes an audio file into a buffer.
|
|
68
|
+
*
|
|
69
|
+
* @return [SoundBuffer]
|
|
70
|
+
* @raise [RuntimeError] if the file cannot be opened or decoded
|
|
71
|
+
*/
|
|
65
72
|
static VALUE SoundBuffer_from_file(VALUE klass, VALUE rb_path) {
|
|
66
73
|
return SoundBuffer_wrap(klass, sfSoundBuffer_createFromFile(StringValueCStr(rb_path)));
|
|
67
74
|
}
|
|
68
75
|
|
|
76
|
+
/* call-seq:
|
|
77
|
+
* SoundBuffer.from_memory(data) -> SoundBuffer
|
|
78
|
+
*
|
|
79
|
+
* Decodes audio held in a String into a buffer.
|
|
80
|
+
*
|
|
81
|
+
* @return [SoundBuffer]
|
|
82
|
+
* @raise [RuntimeError] if +data+ cannot be decoded
|
|
83
|
+
*/
|
|
69
84
|
static VALUE SoundBuffer_from_memory(VALUE klass, VALUE rb_data) {
|
|
70
85
|
StringValue(rb_data);
|
|
71
86
|
|
|
72
|
-
return SoundBuffer_wrap(
|
|
73
|
-
|
|
87
|
+
return SoundBuffer_wrap(
|
|
88
|
+
klass, sfSoundBuffer_createFromMemory(RSTRING_PTR(rb_data), (size_t)RSTRING_LEN(rb_data)));
|
|
74
89
|
}
|
|
75
90
|
|
|
91
|
+
/* call-seq:
|
|
92
|
+
* SoundBuffer.from_stream(stream) -> SoundBuffer
|
|
93
|
+
*
|
|
94
|
+
* Decodes audio read from an SFML InputStream into a buffer.
|
|
95
|
+
*
|
|
96
|
+
* @return [SoundBuffer]
|
|
97
|
+
* @raise [RuntimeError] if the stream cannot be decoded
|
|
98
|
+
*/
|
|
76
99
|
static VALUE SoundBuffer_from_stream(VALUE klass, VALUE rb_stream) {
|
|
77
100
|
VALUE holder = Qnil;
|
|
78
|
-
sfInputStream
|
|
101
|
+
sfInputStream* stream = input_stream_from_rb(rb_stream, &holder);
|
|
79
102
|
|
|
80
|
-
(void)
|
|
103
|
+
(void)holder;
|
|
81
104
|
|
|
82
105
|
return SoundBuffer_wrap(klass, sfSoundBuffer_createFromStream(stream));
|
|
83
106
|
}
|
|
@@ -85,12 +108,13 @@ static VALUE SoundBuffer_from_stream(VALUE klass, VALUE rb_stream) {
|
|
|
85
108
|
/* SFML rejects a buffer whose channel map does not have exactly one entry per
|
|
86
109
|
channel, so an omitted map is filled with the conventional layout for 1 and
|
|
87
110
|
2 channels and left Unspecified beyond that. */
|
|
88
|
-
static void SoundBuffer_default_channel_map(unsigned int channel_count, sfSoundChannel
|
|
111
|
+
static void SoundBuffer_default_channel_map(unsigned int channel_count, sfSoundChannel* map) {
|
|
89
112
|
static const sfSoundChannel surround[] = {
|
|
90
|
-
sfSoundChannelFrontLeft,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
113
|
+
sfSoundChannelFrontLeft, sfSoundChannelFrontRight,
|
|
114
|
+
sfSoundChannelFrontCenter, sfSoundChannelLowFrequencyEffects,
|
|
115
|
+
sfSoundChannelBackLeft, sfSoundChannelBackRight,
|
|
116
|
+
sfSoundChannelSideLeft, sfSoundChannelSideRight,
|
|
117
|
+
sfSoundChannelTopCenter};
|
|
94
118
|
unsigned int i;
|
|
95
119
|
|
|
96
120
|
for (i = 0; i < channel_count; i++) {
|
|
@@ -106,28 +130,76 @@ static void SoundBuffer_default_channel_map(unsigned int channel_count, sfSoundC
|
|
|
106
130
|
}
|
|
107
131
|
}
|
|
108
132
|
|
|
133
|
+
typedef struct {
|
|
134
|
+
sfSoundChannel* channel_map;
|
|
135
|
+
VALUE rb_channel_map;
|
|
136
|
+
unsigned int channel_count;
|
|
137
|
+
} ChannelMapFillContext;
|
|
138
|
+
|
|
139
|
+
/* sound_channel_from_rb raises ArgumentError on an unrecognized entry; run
|
|
140
|
+
through rb_protect so the caller can free the samples/channel_map buffers
|
|
141
|
+
(already allocated by this point) before that exception propagates,
|
|
142
|
+
instead of leaking them. */
|
|
143
|
+
static VALUE SoundBuffer_fill_channel_map(VALUE raw) {
|
|
144
|
+
ChannelMapFillContext* ctx = (ChannelMapFillContext*)raw;
|
|
145
|
+
long i;
|
|
146
|
+
|
|
147
|
+
for (i = 0; i < (long)ctx->channel_count; i++) {
|
|
148
|
+
ctx->channel_map[i] = sound_channel_from_rb(rb_ary_entry(ctx->rb_channel_map, i));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return Qnil;
|
|
152
|
+
}
|
|
153
|
+
|
|
109
154
|
/* Copies the samples into a contiguous int16 buffer: an Array is converted
|
|
110
|
-
element by element, a String
|
|
111
|
-
storage. The channel map, when given, is
|
|
112
|
-
|
|
155
|
+
element by element, and a String's packed int16 samples are copied out of
|
|
156
|
+
the (possibly unaligned) Ruby storage. The channel map, when given, is
|
|
157
|
+
translated symbol by symbol. */
|
|
158
|
+
/* call-seq:
|
|
159
|
+
* SoundBuffer.from_samples(samples, channel_count, sample_rate) -> SoundBuffer
|
|
160
|
+
* SoundBuffer.from_samples(samples, channel_count, sample_rate, channel_map) -> SoundBuffer
|
|
161
|
+
*
|
|
162
|
+
* +samples+ is an Array of Integer samples or a packed String of int16
|
|
163
|
+
* samples, interleaved by channel. +channel_map+, if given, is an Array of
|
|
164
|
+
* channel Symbols (see SoundChannel), one per channel; if omitted, a
|
|
165
|
+
* conventional layout is assumed for 1 and 2 channels.
|
|
166
|
+
*
|
|
167
|
+
* @return [SoundBuffer]
|
|
168
|
+
* @raise [ArgumentError] if +channel_count+ is 0, +samples+ is neither an
|
|
169
|
+
* Array nor a String, or +channel_map+ doesn't have exactly one entry per
|
|
170
|
+
* channel
|
|
171
|
+
*/
|
|
172
|
+
static VALUE SoundBuffer_from_samples(int argc, VALUE* argv, VALUE klass) {
|
|
113
173
|
VALUE rb_samples, rb_channel_count, rb_sample_rate, rb_channel_map;
|
|
114
|
-
int16_t
|
|
115
|
-
sfSoundChannel
|
|
174
|
+
int16_t* samples;
|
|
175
|
+
sfSoundChannel* channel_map;
|
|
116
176
|
unsigned int channel_count;
|
|
117
177
|
uint64_t sample_count;
|
|
118
|
-
sfSoundBuffer
|
|
178
|
+
sfSoundBuffer* buffer;
|
|
119
179
|
|
|
120
180
|
rb_scan_args(argc, argv, "31", &rb_samples, &rb_channel_count, &rb_sample_rate,
|
|
121
181
|
&rb_channel_map);
|
|
122
182
|
|
|
123
|
-
channel_count = (unsigned int)
|
|
183
|
+
channel_count = (unsigned int)NUM2INT(rb_channel_count);
|
|
124
184
|
|
|
125
185
|
if (channel_count == 0) {
|
|
126
186
|
rb_raise(rb_eArgError, "channel count must be positive");
|
|
127
187
|
}
|
|
128
188
|
|
|
189
|
+
/* channel_map's malloc below (sizeof(sfSoundChannel) * channel_count) can
|
|
190
|
+
wrap on the 32-bit targets this gem ships if channel_count is large
|
|
191
|
+
enough, allocating far less than the subsequent write loop assumes.
|
|
192
|
+
Check before any allocation happens, so there's nothing to clean up on
|
|
193
|
+
the raise path. (Written with channel_count as the divisor, not
|
|
194
|
+
compared directly against a constant bound, so this isn't optimized
|
|
195
|
+
away as tautological on 64-bit hosts where it can't actually trigger
|
|
196
|
+
-- it still does real work on the 32-bit targets.) */
|
|
197
|
+
if (SIZE_MAX / channel_count < sizeof(sfSoundChannel)) {
|
|
198
|
+
rb_raise(rb_eArgError, "channel count too large");
|
|
199
|
+
}
|
|
200
|
+
|
|
129
201
|
if (RB_TYPE_P(rb_samples, T_STRING)) {
|
|
130
|
-
size_t bytes = (size_t)
|
|
202
|
+
size_t bytes = (size_t)RSTRING_LEN(rb_samples);
|
|
131
203
|
|
|
132
204
|
if (bytes % sizeof(int16_t) != 0) {
|
|
133
205
|
rb_raise(rb_eArgError, "sample string length must be a multiple of 2 bytes");
|
|
@@ -137,16 +209,21 @@ static VALUE SoundBuffer_from_samples(int argc, VALUE *argv, VALUE klass) {
|
|
|
137
209
|
samples = malloc(bytes > 0 ? bytes : 1);
|
|
138
210
|
|
|
139
211
|
if (bytes > 0) {
|
|
140
|
-
|
|
212
|
+
const int16_t* src = (const int16_t*)RSTRING_PTR(rb_samples);
|
|
213
|
+
size_t i;
|
|
214
|
+
|
|
215
|
+
for (i = 0; i < sample_count; i++) {
|
|
216
|
+
samples[i] = src[i];
|
|
217
|
+
}
|
|
141
218
|
}
|
|
142
219
|
} else if (RB_TYPE_P(rb_samples, T_ARRAY)) {
|
|
143
220
|
long i;
|
|
144
221
|
|
|
145
|
-
sample_count = (uint64_t)
|
|
146
|
-
samples = malloc(sizeof(int16_t) * ((size_t)
|
|
222
|
+
sample_count = (uint64_t)RARRAY_LEN(rb_samples);
|
|
223
|
+
samples = malloc(sizeof(int16_t) * ((size_t)sample_count + 1));
|
|
147
224
|
|
|
148
|
-
for (i = 0; i < (long)
|
|
149
|
-
samples[i] = (int16_t)
|
|
225
|
+
for (i = 0; i < (long)sample_count; i++) {
|
|
226
|
+
samples[i] = (int16_t)NUM2INT(rb_ary_entry(rb_samples, i));
|
|
150
227
|
}
|
|
151
228
|
} else {
|
|
152
229
|
rb_raise(rb_eArgError, "samples must be an Array of integers or a String");
|
|
@@ -158,23 +235,30 @@ static VALUE SoundBuffer_from_samples(int argc, VALUE *argv, VALUE klass) {
|
|
|
158
235
|
if (NIL_P(rb_channel_map)) {
|
|
159
236
|
SoundBuffer_default_channel_map(channel_count, channel_map);
|
|
160
237
|
} else {
|
|
161
|
-
|
|
238
|
+
ChannelMapFillContext ctx = {.channel_map = channel_map,
|
|
239
|
+
.rb_channel_map = rb_channel_map,
|
|
240
|
+
.channel_count = channel_count};
|
|
241
|
+
int state = 0;
|
|
162
242
|
|
|
163
243
|
if (!RB_TYPE_P(rb_channel_map, T_ARRAY) ||
|
|
164
|
-
(unsigned long)
|
|
244
|
+
(unsigned long)RARRAY_LEN(rb_channel_map) != channel_count) {
|
|
165
245
|
free(samples);
|
|
166
246
|
free(channel_map);
|
|
167
247
|
rb_raise(rb_eArgError, "channel map must have exactly one entry per channel");
|
|
168
248
|
}
|
|
169
249
|
|
|
170
|
-
|
|
171
|
-
|
|
250
|
+
rb_protect(SoundBuffer_fill_channel_map, (VALUE)&ctx, &state);
|
|
251
|
+
|
|
252
|
+
if (state) {
|
|
253
|
+
free(samples);
|
|
254
|
+
free(channel_map);
|
|
255
|
+
rb_jump_tag(state);
|
|
172
256
|
}
|
|
173
257
|
}
|
|
174
258
|
|
|
175
259
|
buffer = sfSoundBuffer_createFromSamples(samples, sample_count, channel_count,
|
|
176
|
-
|
|
177
|
-
|
|
260
|
+
(unsigned int)NUM2INT(rb_sample_rate), channel_map,
|
|
261
|
+
channel_count);
|
|
178
262
|
|
|
179
263
|
free(samples);
|
|
180
264
|
free(channel_map);
|
|
@@ -182,17 +266,38 @@ static VALUE SoundBuffer_from_samples(int argc, VALUE *argv, VALUE klass) {
|
|
|
182
266
|
return SoundBuffer_wrap(klass, buffer);
|
|
183
267
|
}
|
|
184
268
|
|
|
269
|
+
/* call-seq: copy -> SoundBuffer
|
|
270
|
+
*
|
|
271
|
+
* Creates an independent copy of the buffer.
|
|
272
|
+
*
|
|
273
|
+
* @return [SoundBuffer] an independent copy
|
|
274
|
+
*/
|
|
185
275
|
static VALUE SoundBuffer_copy(VALUE self) {
|
|
186
276
|
return SoundBuffer_wrap(Get_Klass_SoundBuffer(),
|
|
187
277
|
sfSoundBuffer_copy(Get_SoundBuffer_Struct(self)));
|
|
188
278
|
}
|
|
189
279
|
|
|
280
|
+
/* call-seq:
|
|
281
|
+
* save_to_file(path) -> true or false
|
|
282
|
+
*
|
|
283
|
+
* Writes the buffer to an audio file.
|
|
284
|
+
*
|
|
285
|
+
* @return [Boolean] whether the file was written successfully
|
|
286
|
+
*/
|
|
190
287
|
static VALUE SoundBuffer_save_to_file(VALUE self, VALUE rb_path) {
|
|
191
|
-
return BOOL2RB(
|
|
288
|
+
return BOOL2RB(
|
|
289
|
+
sfSoundBuffer_saveToFile(Get_SoundBuffer_Struct(self), StringValueCStr(rb_path)));
|
|
192
290
|
}
|
|
193
291
|
|
|
292
|
+
/* call-seq: samples -> Array<Integer>
|
|
293
|
+
*
|
|
294
|
+
* Returns the buffer's raw audio samples as Integers.
|
|
295
|
+
*
|
|
296
|
+
* @return [Array<Integer>] the raw int16 audio samples, interleaved by
|
|
297
|
+
* channel
|
|
298
|
+
*/
|
|
194
299
|
static VALUE SoundBuffer_samples(VALUE self) {
|
|
195
|
-
const int16_t
|
|
300
|
+
const int16_t* samples = sfSoundBuffer_getSamples(Get_SoundBuffer_Struct(self));
|
|
196
301
|
uint64_t count = sfSoundBuffer_getSampleCount(Get_SoundBuffer_Struct(self));
|
|
197
302
|
VALUE rb_array;
|
|
198
303
|
uint64_t i;
|
|
@@ -201,7 +306,7 @@ static VALUE SoundBuffer_samples(VALUE self) {
|
|
|
201
306
|
return rb_ary_new();
|
|
202
307
|
}
|
|
203
308
|
|
|
204
|
-
rb_array = rb_ary_new_capa((long)
|
|
309
|
+
rb_array = rb_ary_new_capa((long)count);
|
|
205
310
|
|
|
206
311
|
for (i = 0; i < count; i++) {
|
|
207
312
|
rb_ary_push(rb_array, INT2NUM(samples[i]));
|
|
@@ -210,21 +315,46 @@ static VALUE SoundBuffer_samples(VALUE self) {
|
|
|
210
315
|
return rb_array;
|
|
211
316
|
}
|
|
212
317
|
|
|
318
|
+
/* call-seq: sample_count -> Integer
|
|
319
|
+
*
|
|
320
|
+
* Returns the total number of samples stored in the buffer.
|
|
321
|
+
*
|
|
322
|
+
* @return [Integer] total number of int16 samples, across all channels
|
|
323
|
+
*/
|
|
213
324
|
static VALUE SoundBuffer_sample_count(VALUE self) {
|
|
214
325
|
return ULL2NUM(sfSoundBuffer_getSampleCount(Get_SoundBuffer_Struct(self)));
|
|
215
326
|
}
|
|
216
327
|
|
|
328
|
+
/* call-seq: sample_rate -> Integer
|
|
329
|
+
*
|
|
330
|
+
* Returns the buffer's sample rate in samples per second.
|
|
331
|
+
*
|
|
332
|
+
* @return [Integer]
|
|
333
|
+
*/
|
|
217
334
|
static VALUE SoundBuffer_sample_rate(VALUE self) {
|
|
218
335
|
return UINT2NUM(sfSoundBuffer_getSampleRate(Get_SoundBuffer_Struct(self)));
|
|
219
336
|
}
|
|
220
337
|
|
|
338
|
+
/* call-seq: channel_count -> Integer
|
|
339
|
+
*
|
|
340
|
+
* Returns the number of audio channels in the buffer.
|
|
341
|
+
*
|
|
342
|
+
* @return [Integer]
|
|
343
|
+
*/
|
|
221
344
|
static VALUE SoundBuffer_channel_count(VALUE self) {
|
|
222
345
|
return UINT2NUM(sfSoundBuffer_getChannelCount(Get_SoundBuffer_Struct(self)));
|
|
223
346
|
}
|
|
224
347
|
|
|
348
|
+
/* call-seq: channel_map -> Array<Symbol>
|
|
349
|
+
*
|
|
350
|
+
* Returns the channel layout of the buffer.
|
|
351
|
+
*
|
|
352
|
+
* @return [Array<Symbol>] one entry per channel, e.g.
|
|
353
|
+
* +[:front_left, :front_right]+
|
|
354
|
+
*/
|
|
225
355
|
static VALUE SoundBuffer_channel_map(VALUE self) {
|
|
226
356
|
size_t count = 0;
|
|
227
|
-
sfSoundChannel
|
|
357
|
+
sfSoundChannel* map = sfSoundBuffer_getChannelMap(Get_SoundBuffer_Struct(self), &count);
|
|
228
358
|
VALUE rb_array;
|
|
229
359
|
size_t i;
|
|
230
360
|
|
|
@@ -232,7 +362,7 @@ static VALUE SoundBuffer_channel_map(VALUE self) {
|
|
|
232
362
|
return rb_ary_new();
|
|
233
363
|
}
|
|
234
364
|
|
|
235
|
-
rb_array = rb_ary_new_capa((long)
|
|
365
|
+
rb_array = rb_ary_new_capa((long)count);
|
|
236
366
|
|
|
237
367
|
for (i = 0; i < count; i++) {
|
|
238
368
|
rb_ary_push(rb_array, ID2SYM(rb_intern(sound_channel_name(map[i]))));
|
|
@@ -241,12 +371,32 @@ static VALUE SoundBuffer_channel_map(VALUE self) {
|
|
|
241
371
|
return rb_array;
|
|
242
372
|
}
|
|
243
373
|
|
|
374
|
+
/* call-seq: duration -> Time
|
|
375
|
+
*
|
|
376
|
+
* Returns the buffer's total playing duration.
|
|
377
|
+
*
|
|
378
|
+
* @return [Time]
|
|
379
|
+
*/
|
|
244
380
|
static VALUE SoundBuffer_duration(VALUE self) {
|
|
245
381
|
return time_to_rb(sfSoundBuffer_getDuration(Get_SoundBuffer_Struct(self)));
|
|
246
382
|
}
|
|
247
383
|
|
|
248
|
-
|
|
249
|
-
|
|
384
|
+
static VALUE SoundBuffer_alloc(VALUE klass) {
|
|
385
|
+
(void)klass;
|
|
386
|
+
rb_raise(rb_eNotImpError,
|
|
387
|
+
"use SoundBuffer.from_file, SoundBuffer.from_memory, SoundBuffer.from_stream or "
|
|
388
|
+
"SoundBuffer.from_samples");
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/* Document-class: SFML::SoundBuffer
|
|
392
|
+
* Audio samples held fully decoded in memory, ready to be played through one
|
|
393
|
+
* or more Sound instances (a single buffer may back several simultaneous
|
|
394
|
+
* Sounds). For long audio, prefer Music, which streams instead of loading
|
|
395
|
+
* everything up front.
|
|
396
|
+
*/
|
|
397
|
+
void Init_SoundBuffer(VALUE rb_mSFML) {
|
|
398
|
+
rb_cSoundBuffer = rb_define_class_under(rb_mSFML, "SoundBuffer", rb_cObject);
|
|
399
|
+
rb_define_alloc_func(rb_cSoundBuffer, SoundBuffer_alloc);
|
|
250
400
|
|
|
251
401
|
rb_define_singleton_method(rb_cSoundBuffer, "from_file", SoundBuffer_from_file, 1);
|
|
252
402
|
rb_define_singleton_method(rb_cSoundBuffer, "from_memory", SoundBuffer_from_memory, 1);
|
|
@@ -267,8 +417,8 @@ VALUE Get_Klass_SoundBuffer(void) {
|
|
|
267
417
|
return rb_cSoundBuffer;
|
|
268
418
|
}
|
|
269
419
|
|
|
270
|
-
void
|
|
271
|
-
SoundBuffer
|
|
420
|
+
void* Get_SoundBuffer_Struct(VALUE self) {
|
|
421
|
+
SoundBuffer* ptr;
|
|
272
422
|
TypedData_Get_Struct(self, SoundBuffer, &SoundBuffer_data_type, ptr);
|
|
273
423
|
return ptr->buffer;
|
|
274
424
|
}
|