sfml3-rb 0.0.1 → 0.2.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.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +321 -0
  3. data/README.md +44 -14
  4. data/TODO.md +199 -0
  5. data/ext/audio/audio_enums.c +131 -0
  6. data/ext/audio/audio_enums.h +18 -0
  7. data/ext/audio/effect_processor.c +233 -0
  8. data/ext/audio/effect_processor.h +25 -0
  9. data/ext/audio/listener.c +83 -0
  10. data/ext/audio/listener.h +8 -0
  11. data/ext/audio/music.c +174 -0
  12. data/ext/audio/music.h +14 -0
  13. data/ext/audio/sound.c +139 -0
  14. data/ext/audio/sound.h +14 -0
  15. data/ext/audio/sound_buffer.c +274 -0
  16. data/ext/audio/sound_buffer.h +18 -0
  17. data/ext/audio/sound_buffer_recorder.c +109 -0
  18. data/ext/audio/sound_buffer_recorder.h +14 -0
  19. data/ext/audio/sound_recorder.c +268 -0
  20. data/ext/audio/sound_recorder.h +14 -0
  21. data/ext/audio/sound_source.h +13 -0
  22. data/ext/audio/sound_source.inc +271 -0
  23. data/ext/audio/sound_source_cone.c +138 -0
  24. data/ext/audio/sound_source_cone.h +20 -0
  25. data/ext/audio/sound_stream.c +294 -0
  26. data/ext/audio/sound_stream.h +14 -0
  27. data/ext/{exceptions.c → core/exceptions.c} +10 -8
  28. data/ext/{ext → core}/exceptions.h +3 -3
  29. data/ext/core/foreign_thread.c +149 -0
  30. data/ext/core/foreign_thread.h +31 -0
  31. data/ext/core/macros.h +8 -0
  32. data/ext/{ext → core}/sfml.h +5 -3
  33. data/ext/core/unicode.c +46 -0
  34. data/ext/core/unicode.h +25 -0
  35. data/ext/ext.c +125 -14
  36. data/ext/extconf.rb +51 -6
  37. data/ext/graphics/blend_mode.c +261 -0
  38. data/ext/graphics/blend_mode.h +18 -0
  39. data/ext/graphics/circle.c +319 -0
  40. data/ext/{ext/klass/drawable → graphics}/circle.h +3 -3
  41. data/ext/graphics/color.c +318 -0
  42. data/ext/graphics/color.h +37 -0
  43. data/ext/{drawable.c → graphics/drawable.c} +11 -11
  44. data/ext/graphics/drawable.h +10 -0
  45. data/ext/graphics/enums.c +123 -0
  46. data/ext/graphics/enums.h +20 -0
  47. data/ext/graphics/font.c +148 -0
  48. data/ext/graphics/font.h +14 -0
  49. data/ext/graphics/glyph.c +60 -0
  50. data/ext/graphics/glyph.h +14 -0
  51. data/ext/graphics/image.c +205 -0
  52. data/ext/graphics/image.h +16 -0
  53. data/ext/graphics/polygon.c +300 -0
  54. data/ext/graphics/polygon.h +10 -0
  55. data/ext/graphics/rect.c +287 -0
  56. data/ext/graphics/rect.h +30 -0
  57. data/ext/graphics/rectangle.c +302 -0
  58. data/ext/graphics/rectangle.h +10 -0
  59. data/ext/graphics/render_state.c +204 -0
  60. data/ext/graphics/render_state.h +18 -0
  61. data/ext/graphics/render_target.inc +181 -0
  62. data/ext/graphics/render_texture.c +182 -0
  63. data/ext/graphics/render_texture.h +14 -0
  64. data/ext/graphics/shader.c +540 -0
  65. data/ext/graphics/shader.h +14 -0
  66. data/ext/graphics/shape.c +311 -0
  67. data/ext/graphics/shape.h +10 -0
  68. data/ext/graphics/sprite.c +249 -0
  69. data/ext/graphics/sprite.h +10 -0
  70. data/ext/graphics/stencil_mode.c +211 -0
  71. data/ext/graphics/stencil_mode.h +18 -0
  72. data/ext/graphics/target.c +133 -0
  73. data/ext/graphics/target.h +40 -0
  74. data/ext/graphics/text.c +344 -0
  75. data/ext/graphics/text.h +10 -0
  76. data/ext/graphics/texture.c +379 -0
  77. data/ext/graphics/texture.h +16 -0
  78. data/ext/graphics/transform.c +372 -0
  79. data/ext/graphics/transform.h +35 -0
  80. data/ext/{transformable.c → graphics/transformable.c} +168 -144
  81. data/ext/{ext/klass → graphics}/transformable.h +3 -3
  82. data/ext/graphics/vertex.c +179 -0
  83. data/ext/graphics/vertex.h +23 -0
  84. data/ext/graphics/vertex_array.c +127 -0
  85. data/ext/graphics/vertex_array.h +10 -0
  86. data/ext/graphics/vertex_buffer.c +211 -0
  87. data/ext/graphics/vertex_buffer.h +14 -0
  88. data/ext/{view.c → graphics/view.c} +168 -169
  89. data/ext/{ext/klass → graphics}/view.h +3 -3
  90. data/ext/network/ftp.c +405 -0
  91. data/ext/network/ftp.h +20 -0
  92. data/ext/network/http.c +243 -0
  93. data/ext/network/http.h +18 -0
  94. data/ext/network/ip_address.c +164 -0
  95. data/ext/network/ip_address.h +20 -0
  96. data/ext/network/network_enums.c +298 -0
  97. data/ext/network/network_enums.h +26 -0
  98. data/ext/network/packet.c +189 -0
  99. data/ext/network/packet.h +14 -0
  100. data/ext/network/socket_selector.c +142 -0
  101. data/ext/network/socket_selector.h +14 -0
  102. data/ext/network/tcp_listener.c +115 -0
  103. data/ext/network/tcp_listener.h +14 -0
  104. data/ext/network/tcp_socket.c +199 -0
  105. data/ext/network/tcp_socket.h +17 -0
  106. data/ext/network/udp_socket.c +190 -0
  107. data/ext/network/udp_socket.h +14 -0
  108. data/ext/ports.rb +88 -21
  109. data/ext/system/buffer.c +77 -0
  110. data/ext/system/buffer.h +14 -0
  111. data/ext/system/clock.c +100 -0
  112. data/ext/{ext/klass → system}/clock.h +3 -3
  113. data/ext/system/input_stream.c +229 -0
  114. data/ext/system/input_stream.h +23 -0
  115. data/ext/system/sleep.c +16 -0
  116. data/ext/system/sleep.h +8 -0
  117. data/ext/system/time.c +218 -0
  118. data/ext/system/time.h +18 -0
  119. data/ext/system/vec2.c +251 -0
  120. data/ext/system/vec2.h +55 -0
  121. data/ext/system/vec3.c +219 -0
  122. data/ext/system/vec3.h +18 -0
  123. data/ext/window/clipboard.c +89 -0
  124. data/ext/window/clipboard.h +8 -0
  125. data/ext/window/context.c +70 -0
  126. data/ext/window/context.h +8 -0
  127. data/ext/window/context_settings.c +220 -0
  128. data/ext/window/context_settings.h +18 -0
  129. data/ext/window/cursor.c +56 -0
  130. data/ext/window/cursor.h +14 -0
  131. data/ext/window/event.c +216 -0
  132. data/ext/window/event.h +14 -0
  133. data/ext/{event_name.c → window/event_name.c} +2 -2
  134. data/ext/window/event_name.h +8 -0
  135. data/ext/window/input_enums.c +261 -0
  136. data/ext/window/input_enums.h +36 -0
  137. data/ext/window/joystick.c +60 -0
  138. data/ext/window/joystick.h +8 -0
  139. data/ext/{keyboard.c → window/keyboard.c} +74 -2
  140. data/ext/window/keyboard.h +12 -0
  141. data/ext/window/mouse.c +70 -0
  142. data/ext/window/mouse.h +8 -0
  143. data/ext/window/sensor.c +30 -0
  144. data/ext/window/sensor.h +8 -0
  145. data/ext/window/touch.c +49 -0
  146. data/ext/window/touch.h +8 -0
  147. data/ext/window/video_mode.c +133 -0
  148. data/ext/window/video_mode.h +14 -0
  149. data/ext/window/vulkan.c +44 -0
  150. data/ext/window/vulkan.h +8 -0
  151. data/ext/window/window.c +414 -0
  152. data/ext/{ext/klass → window}/window.h +3 -3
  153. data/lib/sfml/version.rb +4 -2
  154. data/lib/sfml.rb +2 -0
  155. metadata +163 -56
  156. data/ext/circle.c +0 -198
  157. data/ext/clock.c +0 -65
  158. data/ext/color.c +0 -43
  159. data/ext/event.c +0 -148
  160. data/ext/ext/klass/color.h +0 -26
  161. data/ext/ext/klass/drawable/image.h +0 -4
  162. data/ext/ext/klass/drawable/polygon.h +0 -4
  163. data/ext/ext/klass/drawable/rectangle.h +0 -4
  164. data/ext/ext/klass/drawable/sprite.h +0 -4
  165. data/ext/ext/klass/drawable/texture.h +0 -4
  166. data/ext/ext/klass/event/event_name.h +0 -8
  167. data/ext/ext/klass/event.h +0 -12
  168. data/ext/ext/klass/keyboard.h +0 -8
  169. data/ext/ext/klass/render_state.h +0 -16
  170. data/ext/ext/klass/target.h +0 -18
  171. data/ext/ext/klass/transform.h +0 -17
  172. data/ext/ext/klass/video_mode.h +0 -12
  173. data/ext/ext/module/drawable/drawable.h +0 -10
  174. data/ext/ext/module/transform.h +0 -18
  175. data/ext/ext/module.h +0 -10
  176. data/ext/ext/rect.h +0 -16
  177. data/ext/ext/style_name.h +0 -11
  178. data/ext/ext/vec2.h +0 -38
  179. data/ext/image.c +0 -5
  180. data/ext/polygon.c +0 -5
  181. data/ext/rect.c +0 -26
  182. data/ext/rectangle.c +0 -5
  183. data/ext/render_state.c +0 -109
  184. data/ext/sprite.c +0 -5
  185. data/ext/style_name.c +0 -15
  186. data/ext/target.c +0 -106
  187. data/ext/texture.c +0 -5
  188. data/ext/transform.c +0 -107
  189. data/ext/vec2.c +0 -42
  190. data/ext/video_mode.c +0 -78
  191. data/ext/window.c +0 -274
@@ -0,0 +1,131 @@
1
+ #include "audio/audio_enums.h"
2
+
3
+ #include <ruby.h>
4
+ #include <string.h>
5
+
6
+ static const char *sound_status_names[] = {"stopped", "paused", "playing"};
7
+
8
+ /* Designated initializers, indexed by the enum constant rather than by
9
+ position: CSFML 3 defines the channel order, and a positional table would
10
+ silently return the wrong name if a future release inserted a value. */
11
+ static const char *sound_channel_names[] = {
12
+ [sfSoundChannelUnspecified] = "unspecified",
13
+ [sfSoundChannelMono] = "mono",
14
+ [sfSoundChannelFrontLeft] = "front_left",
15
+ [sfSoundChannelFrontRight] = "front_right",
16
+ [sfSoundChannelFrontCenter] = "front_center",
17
+ [sfSoundChannelFrontLeftOfCenter] = "front_left_of_center",
18
+ [sfSoundChannelFrontRightOfCenter] = "front_right_of_center",
19
+ [sfSoundChannelLowFrequencyEffects] = "low_frequency_effects",
20
+ [sfSoundChannelBackLeft] = "back_left",
21
+ [sfSoundChannelBackRight] = "back_right",
22
+ [sfSoundChannelBackCenter] = "back_center",
23
+ [sfSoundChannelSideLeft] = "side_left",
24
+ [sfSoundChannelSideRight] = "side_right",
25
+ [sfSoundChannelTopCenter] = "top_center",
26
+ [sfSoundChannelTopFrontLeft] = "top_front_left",
27
+ [sfSoundChannelTopFrontRight] = "top_front_right",
28
+ [sfSoundChannelTopFrontCenter] = "top_front_center",
29
+ [sfSoundChannelTopBackLeft] = "top_back_left",
30
+ [sfSoundChannelTopBackRight] = "top_back_right",
31
+ [sfSoundChannelTopBackCenter] = "top_back_center"
32
+ };
33
+
34
+ static int symbol_index(VALUE rb_symbol, const char *const *names, size_t count) {
35
+ const char *name;
36
+ size_t i;
37
+
38
+ if (!SYMBOL_P(rb_symbol)) {
39
+ return -1;
40
+ }
41
+
42
+ name = rb_id2name(SYM2ID(rb_symbol));
43
+
44
+ for (i = 0; i < count; i++) {
45
+ if (names[i] != NULL && strcmp(name, names[i]) == 0) {
46
+ return (int) i;
47
+ }
48
+ }
49
+
50
+ return -1;
51
+ }
52
+
53
+ const char *sound_status_name(sfSoundStatus status) {
54
+ if (status >= sfStopped && status <= sfPlaying) {
55
+ return sound_status_names[status];
56
+ }
57
+
58
+ return "stopped";
59
+ }
60
+
61
+ sfSoundStatus sound_status_from_rb(VALUE rb_status) {
62
+ int index;
63
+
64
+ if (RB_INTEGER_TYPE_P(rb_status)) {
65
+ return (sfSoundStatus) NUM2INT(rb_status);
66
+ }
67
+
68
+ index = symbol_index(rb_status, sound_status_names, 3);
69
+
70
+ if (index < 0) {
71
+ rb_raise(rb_eArgError, "unknown sound status");
72
+ }
73
+
74
+ return (sfSoundStatus) index;
75
+ }
76
+
77
+ const char *sound_channel_name(sfSoundChannel channel) {
78
+ if (channel >= sfSoundChannelUnspecified && channel <= sfSoundChannelTopBackCenter &&
79
+ sound_channel_names[channel] != NULL) {
80
+ return sound_channel_names[channel];
81
+ }
82
+
83
+ return "unspecified";
84
+ }
85
+
86
+ sfSoundChannel sound_channel_from_rb(VALUE rb_channel) {
87
+ int index;
88
+
89
+ if (RB_INTEGER_TYPE_P(rb_channel)) {
90
+ return (sfSoundChannel) NUM2INT(rb_channel);
91
+ }
92
+
93
+ index = symbol_index(rb_channel, sound_channel_names,
94
+ sizeof(sound_channel_names) / sizeof(sound_channel_names[0]));
95
+
96
+ if (index < 0) {
97
+ rb_raise(rb_eArgError, "unknown sound channel");
98
+ }
99
+
100
+ return (sfSoundChannel) index;
101
+ }
102
+
103
+ void Init_AudioEnums(VALUE rb_module) {
104
+ VALUE rb_mSoundStatus = rb_define_module_under(rb_module, "SoundStatus");
105
+ VALUE rb_mSoundChannel = rb_define_module_under(rb_module, "SoundChannel");
106
+
107
+ rb_define_const(rb_mSoundStatus, "STOPPED", INT2NUM(sfStopped));
108
+ rb_define_const(rb_mSoundStatus, "PAUSED", INT2NUM(sfPaused));
109
+ rb_define_const(rb_mSoundStatus, "PLAYING", INT2NUM(sfPlaying));
110
+
111
+ rb_define_const(rb_mSoundChannel, "UNSPECIFIED", INT2NUM(sfSoundChannelUnspecified));
112
+ rb_define_const(rb_mSoundChannel, "MONO", INT2NUM(sfSoundChannelMono));
113
+ rb_define_const(rb_mSoundChannel, "FRONT_LEFT", INT2NUM(sfSoundChannelFrontLeft));
114
+ rb_define_const(rb_mSoundChannel, "FRONT_RIGHT", INT2NUM(sfSoundChannelFrontRight));
115
+ rb_define_const(rb_mSoundChannel, "FRONT_CENTER", INT2NUM(sfSoundChannelFrontCenter));
116
+ rb_define_const(rb_mSoundChannel, "FRONT_LEFT_OF_CENTER", INT2NUM(sfSoundChannelFrontLeftOfCenter));
117
+ rb_define_const(rb_mSoundChannel, "FRONT_RIGHT_OF_CENTER", INT2NUM(sfSoundChannelFrontRightOfCenter));
118
+ rb_define_const(rb_mSoundChannel, "LOW_FREQUENCY_EFFECTS", INT2NUM(sfSoundChannelLowFrequencyEffects));
119
+ rb_define_const(rb_mSoundChannel, "BACK_LEFT", INT2NUM(sfSoundChannelBackLeft));
120
+ rb_define_const(rb_mSoundChannel, "BACK_RIGHT", INT2NUM(sfSoundChannelBackRight));
121
+ rb_define_const(rb_mSoundChannel, "BACK_CENTER", INT2NUM(sfSoundChannelBackCenter));
122
+ rb_define_const(rb_mSoundChannel, "SIDE_LEFT", INT2NUM(sfSoundChannelSideLeft));
123
+ rb_define_const(rb_mSoundChannel, "SIDE_RIGHT", INT2NUM(sfSoundChannelSideRight));
124
+ rb_define_const(rb_mSoundChannel, "TOP_CENTER", INT2NUM(sfSoundChannelTopCenter));
125
+ rb_define_const(rb_mSoundChannel, "TOP_FRONT_LEFT", INT2NUM(sfSoundChannelTopFrontLeft));
126
+ rb_define_const(rb_mSoundChannel, "TOP_FRONT_RIGHT", INT2NUM(sfSoundChannelTopFrontRight));
127
+ rb_define_const(rb_mSoundChannel, "TOP_FRONT_CENTER", INT2NUM(sfSoundChannelTopFrontCenter));
128
+ rb_define_const(rb_mSoundChannel, "TOP_BACK_LEFT", INT2NUM(sfSoundChannelTopBackLeft));
129
+ rb_define_const(rb_mSoundChannel, "TOP_BACK_RIGHT", INT2NUM(sfSoundChannelTopBackRight));
130
+ rb_define_const(rb_mSoundChannel, "TOP_BACK_CENTER", INT2NUM(sfSoundChannelTopBackCenter));
131
+ }
@@ -0,0 +1,18 @@
1
+ #ifndef SFML_RB_AUDIO_AUDIO_ENUMS_H
2
+ #define SFML_RB_AUDIO_AUDIO_ENUMS_H
3
+
4
+ #include <ruby.h>
5
+
6
+ #include "core/sfml.h"
7
+
8
+ void Init_AudioEnums(VALUE rb_module);
9
+
10
+ const char *sound_status_name(sfSoundStatus status);
11
+
12
+ sfSoundStatus sound_status_from_rb(VALUE rb_status);
13
+
14
+ const char *sound_channel_name(sfSoundChannel channel);
15
+
16
+ sfSoundChannel sound_channel_from_rb(VALUE rb_channel);
17
+
18
+ #endif //SFML_RB_AUDIO_AUDIO_ENUMS_H
@@ -0,0 +1,233 @@
1
+ #include "audio/effect_processor.h"
2
+
3
+ #include <ruby.h>
4
+ #include <string.h>
5
+
6
+ #include "core/foreign_thread.h"
7
+
8
+ /* Slots are process-global because a C callback cannot carry context. Each
9
+ entry is either Qnil (free) or the Proc assigned to that slot. */
10
+ static VALUE effect_processors[EFFECT_PROCESSOR_SLOTS];
11
+
12
+ typedef struct {
13
+ int slot;
14
+ const float* input;
15
+ unsigned int input_count;
16
+ float* output;
17
+ unsigned int output_count;
18
+ unsigned int requested_output_count;
19
+ unsigned int channels;
20
+ } EffectContext;
21
+
22
+ /* Fills the output buffer with silence for the full frame count the audio
23
+ engine asked for. Used whenever the processor cannot produce real output
24
+ (an exception, or a timed-out worker): reporting output_count 0 instead of
25
+ this was observed to make miniaudio's node graph re-invoke this callback
26
+ in an immediate, unbounded retry loop (tens of thousands of calls/second)
27
+ rather than treat it as one completed (silent) buffer - which in turn
28
+ starved SFML's AudioDevice::waitForReadingComplete() on the main thread,
29
+ since the audio thread never stopped re-acquiring its internal mutex. */
30
+ static void effect_output_silence(EffectContext* ctx) {
31
+ unsigned int i;
32
+
33
+ for (i = 0; i < ctx->requested_output_count * ctx->channels; i++) {
34
+ ctx->output[i] = 0.0f;
35
+ }
36
+
37
+ ctx->output_count = ctx->requested_output_count;
38
+ }
39
+
40
+ #define EFFECT_JOB_TIMEOUT_MS 50
41
+
42
+ void Init_EffectProcessor(void) {
43
+ int i;
44
+
45
+ for (i = 0; i < EFFECT_PROCESSOR_SLOTS; i++) {
46
+ effect_processors[i] = Qnil;
47
+ rb_gc_register_address(&effect_processors[i]);
48
+ }
49
+ }
50
+
51
+ static VALUE effect_yield(VALUE raw) {
52
+ EffectContext* ctx = (EffectContext*)raw;
53
+ VALUE rb_input;
54
+ VALUE rb_result;
55
+ unsigned int i;
56
+ unsigned int c;
57
+ long produced;
58
+
59
+ rb_input = rb_ary_new_capa((long)ctx->input_count);
60
+
61
+ for (i = 0; i < ctx->input_count; i++) {
62
+ VALUE frame = rb_ary_new_capa((long)ctx->channels);
63
+
64
+ for (c = 0; c < ctx->channels; c++) {
65
+ rb_ary_push(frame, DBL2NUM((double)ctx->input[i * ctx->channels + c]));
66
+ }
67
+
68
+ rb_ary_push(rb_input, frame);
69
+ }
70
+
71
+ rb_result = rb_funcall(effect_processors[ctx->slot], rb_intern("call"), 2, rb_input,
72
+ UINT2NUM(ctx->channels));
73
+
74
+ if (!RB_TYPE_P(rb_result, T_ARRAY)) {
75
+ rb_raise(rb_eTypeError, "effect processor must return an Array of frames");
76
+ }
77
+
78
+ produced = RARRAY_LEN(rb_result);
79
+
80
+ if (produced > (long)ctx->output_count) {
81
+ produced = (long)ctx->output_count;
82
+ }
83
+
84
+ for (i = 0; i < (unsigned int)produced; i++) {
85
+ VALUE frame = rb_ary_entry(rb_result, (long)i);
86
+
87
+ if (!RB_TYPE_P(frame, T_ARRAY)) {
88
+ rb_raise(rb_eTypeError, "effect processor frames must be Arrays");
89
+ }
90
+
91
+ for (c = 0; c < ctx->channels; c++) {
92
+ ctx->output[i * ctx->channels + c] = (float)NUM2DBL(rb_ary_entry(frame, (long)c));
93
+ }
94
+ }
95
+
96
+ ctx->output_count = (unsigned int)produced;
97
+
98
+ return Qnil;
99
+ }
100
+
101
+ /* Runs on the shared foreign-thread worker, which holds the GVL. */
102
+ static void effect_run(void* raw) {
103
+ EffectContext* ctx = (EffectContext*)raw;
104
+ int state = 0;
105
+
106
+ rb_protect(effect_yield, (VALUE)ctx, &state);
107
+
108
+ if (state) {
109
+ /* An exception must not unwind through the audio engine: drop the
110
+ output and let playback continue silently. */
111
+ rb_set_errinfo(Qnil);
112
+ effect_output_silence(ctx);
113
+ }
114
+ }
115
+
116
+ static void effect_invoke(int slot, const float* input, unsigned int* input_count, float* output,
117
+ unsigned int* output_count, unsigned int channels) {
118
+ EffectContext ctx;
119
+
120
+ if (slot < 0 || slot >= EFFECT_PROCESSOR_SLOTS || NIL_P(effect_processors[slot]) ||
121
+ *output_count == 0) {
122
+ *output_count = 0;
123
+ return;
124
+ }
125
+
126
+ ctx.slot = slot;
127
+ ctx.input = input;
128
+ ctx.input_count = (input != NULL) ? *input_count : 0;
129
+ ctx.output = output;
130
+ ctx.output_count = *output_count;
131
+ ctx.requested_output_count = *output_count;
132
+ ctx.channels = channels;
133
+
134
+ if (!run_on_ruby_thread(effect_run, &ctx, EFFECT_JOB_TIMEOUT_MS)) {
135
+ /* Timed out before the worker even claimed the job (e.g. GVL
136
+ contention): degrade to silence for this callback rather than
137
+ stall the audio thread. */
138
+ effect_output_silence(&ctx);
139
+ }
140
+
141
+ /* The processor consumed every input frame it was offered. */
142
+ *input_count = ctx.input_count;
143
+ *output_count = ctx.output_count;
144
+ }
145
+
146
+ #define EFFECT_THUNK(n) \
147
+ static void effect_thunk_##n(const float* input, unsigned int* input_count, float* output, \
148
+ unsigned int* output_count, unsigned int channels) { \
149
+ effect_invoke(n, input, input_count, output, output_count, channels); \
150
+ }
151
+
152
+ EFFECT_THUNK(0)
153
+ EFFECT_THUNK(1)
154
+ EFFECT_THUNK(2)
155
+ EFFECT_THUNK(3)
156
+ EFFECT_THUNK(4)
157
+ EFFECT_THUNK(5)
158
+ EFFECT_THUNK(6)
159
+ EFFECT_THUNK(7)
160
+ EFFECT_THUNK(8)
161
+ EFFECT_THUNK(9)
162
+ EFFECT_THUNK(10)
163
+ EFFECT_THUNK(11)
164
+ EFFECT_THUNK(12)
165
+ EFFECT_THUNK(13)
166
+ EFFECT_THUNK(14)
167
+ EFFECT_THUNK(15)
168
+ EFFECT_THUNK(16)
169
+ EFFECT_THUNK(17)
170
+ EFFECT_THUNK(18)
171
+ EFFECT_THUNK(19)
172
+ EFFECT_THUNK(20)
173
+ EFFECT_THUNK(21)
174
+ EFFECT_THUNK(22)
175
+ EFFECT_THUNK(23)
176
+ EFFECT_THUNK(24)
177
+ EFFECT_THUNK(25)
178
+ EFFECT_THUNK(26)
179
+ EFFECT_THUNK(27)
180
+ EFFECT_THUNK(28)
181
+ EFFECT_THUNK(29)
182
+ EFFECT_THUNK(30)
183
+ EFFECT_THUNK(31)
184
+
185
+ static sfEffectProcessor effect_thunks[EFFECT_PROCESSOR_SLOTS] = {
186
+ effect_thunk_0, effect_thunk_1, effect_thunk_2, effect_thunk_3, effect_thunk_4,
187
+ effect_thunk_5, effect_thunk_6, effect_thunk_7, effect_thunk_8, effect_thunk_9,
188
+ effect_thunk_10, effect_thunk_11, effect_thunk_12, effect_thunk_13, effect_thunk_14,
189
+ effect_thunk_15, effect_thunk_16, effect_thunk_17, effect_thunk_18, effect_thunk_19,
190
+ effect_thunk_20, effect_thunk_21, effect_thunk_22, effect_thunk_23, effect_thunk_24,
191
+ effect_thunk_25, effect_thunk_26, effect_thunk_27, effect_thunk_28, effect_thunk_29,
192
+ effect_thunk_30, effect_thunk_31};
193
+
194
+ sfEffectProcessor effect_processor_acquire(VALUE rb_proc, int* slot) {
195
+ int i;
196
+
197
+ /* Replacing the processor on a source that already has one reuses its
198
+ slot, so a source never leaks slots across repeated assignment. */
199
+ if (*slot >= 0 && *slot < EFFECT_PROCESSOR_SLOTS) {
200
+ if (NIL_P(rb_proc)) {
201
+ effect_processors[*slot] = Qnil;
202
+ return NULL;
203
+ }
204
+
205
+ effect_processors[*slot] = rb_proc;
206
+
207
+ return effect_thunks[*slot];
208
+ }
209
+
210
+ if (NIL_P(rb_proc)) {
211
+ *slot = -1;
212
+ return NULL;
213
+ }
214
+
215
+ for (i = 0; i < EFFECT_PROCESSOR_SLOTS; i++) {
216
+ if (NIL_P(effect_processors[i])) {
217
+ effect_processors[i] = rb_proc;
218
+ *slot = i;
219
+
220
+ return effect_thunks[i];
221
+ }
222
+ }
223
+
224
+ rb_raise(rb_eRuntimeError, "all %d effect-processor slots are in use", EFFECT_PROCESSOR_SLOTS);
225
+
226
+ return NULL;
227
+ }
228
+
229
+ void effect_processor_release(int slot) {
230
+ if (slot >= 0 && slot < EFFECT_PROCESSOR_SLOTS) {
231
+ effect_processors[slot] = Qnil;
232
+ }
233
+ }
@@ -0,0 +1,25 @@
1
+ #ifndef SFML_RB_AUDIO_EFFECT_PROCESSOR_H
2
+ #define SFML_RB_AUDIO_EFFECT_PROCESSOR_H
3
+
4
+ #include <ruby.h>
5
+
6
+ #include "core/sfml.h"
7
+
8
+ /* sfEffectProcessor carries no userData, so a single C callback cannot tell
9
+ which sound source invoked it. Instead the binding keeps a fixed pool of
10
+ identical C thunks, each hard-wired to one slot; assigning a processor to a
11
+ source hands it the thunk for that source's slot. Slots are bounded, and
12
+ exceeding the pool raises rather than silently sharing one callback. */
13
+ #define EFFECT_PROCESSOR_SLOTS 32
14
+
15
+ /* Registers every slot as a GC root. Must run before any acquire. */
16
+ void Init_EffectProcessor(void);
17
+
18
+ /* Stores rb_proc in a free slot, writes its index to *slot and returns the
19
+ matching thunk. Passing nil returns NULL (no processor). */
20
+ sfEffectProcessor effect_processor_acquire(VALUE rb_proc, int *slot);
21
+
22
+ /* Frees a slot previously handed out, clearing the Ruby reference. */
23
+ void effect_processor_release(int slot);
24
+
25
+ #endif //SFML_RB_AUDIO_EFFECT_PROCESSOR_H
@@ -0,0 +1,83 @@
1
+ #include "audio/listener.h"
2
+
3
+ #include <ruby.h>
4
+
5
+ #include "audio/sound_source_cone.h"
6
+ #include "system/vec3.h"
7
+
8
+ static VALUE Listener_global_volume(VALUE module) {
9
+ return DBL2NUM(sfListener_getGlobalVolume());
10
+ }
11
+
12
+ static VALUE Listener_set_global_volume(VALUE module, VALUE rb_value) {
13
+ sfListener_setGlobalVolume((float) NUM2DBL(rb_value));
14
+ return rb_value;
15
+ }
16
+
17
+ static VALUE Listener_position(VALUE module) {
18
+ return vec3f_to_rb(sfListener_getPosition());
19
+ }
20
+
21
+ static VALUE Listener_set_position(VALUE module, VALUE rb_value) {
22
+ sfListener_setPosition(vec3f_from_rb(rb_value));
23
+ return rb_value;
24
+ }
25
+
26
+ static VALUE Listener_direction(VALUE module) {
27
+ return vec3f_to_rb(sfListener_getDirection());
28
+ }
29
+
30
+ static VALUE Listener_set_direction(VALUE module, VALUE rb_value) {
31
+ sfListener_setDirection(vec3f_from_rb(rb_value));
32
+ return rb_value;
33
+ }
34
+
35
+ static VALUE Listener_velocity(VALUE module) {
36
+ return vec3f_to_rb(sfListener_getVelocity());
37
+ }
38
+
39
+ static VALUE Listener_set_velocity(VALUE module, VALUE rb_value) {
40
+ sfListener_setVelocity(vec3f_from_rb(rb_value));
41
+ return rb_value;
42
+ }
43
+
44
+ static VALUE Listener_up_vector(VALUE module) {
45
+ return vec3f_to_rb(sfListener_getUpVector());
46
+ }
47
+
48
+ static VALUE Listener_set_up_vector(VALUE module, VALUE rb_value) {
49
+ sfListener_setUpVector(vec3f_from_rb(rb_value));
50
+ return rb_value;
51
+ }
52
+
53
+ static VALUE Listener_cone(VALUE module) {
54
+ sfListenerCone cone = sfListener_getCone();
55
+
56
+ return sound_source_cone_to_rb((sfSoundSourceCone) {cone.innerAngle, cone.outerAngle,
57
+ cone.outerGain});
58
+ }
59
+
60
+ static VALUE Listener_set_cone(VALUE module, VALUE rb_value) {
61
+ sfSoundSourceCone cone = sound_source_cone_from_rb(rb_value);
62
+
63
+ sfListener_setCone((sfListenerCone) {cone.innerAngle, cone.outerAngle, cone.outerGain});
64
+
65
+ return rb_value;
66
+ }
67
+
68
+ void Init_Listener(VALUE rb_module) {
69
+ VALUE rb_mListener = rb_define_module_under(rb_module, "Listener");
70
+
71
+ rb_define_module_function(rb_mListener, "global_volume", Listener_global_volume, 0);
72
+ rb_define_module_function(rb_mListener, "global_volume=", Listener_set_global_volume, 1);
73
+ rb_define_module_function(rb_mListener, "position", Listener_position, 0);
74
+ rb_define_module_function(rb_mListener, "position=", Listener_set_position, 1);
75
+ rb_define_module_function(rb_mListener, "direction", Listener_direction, 0);
76
+ rb_define_module_function(rb_mListener, "direction=", Listener_set_direction, 1);
77
+ rb_define_module_function(rb_mListener, "velocity", Listener_velocity, 0);
78
+ rb_define_module_function(rb_mListener, "velocity=", Listener_set_velocity, 1);
79
+ rb_define_module_function(rb_mListener, "up_vector", Listener_up_vector, 0);
80
+ rb_define_module_function(rb_mListener, "up_vector=", Listener_set_up_vector, 1);
81
+ rb_define_module_function(rb_mListener, "cone", Listener_cone, 0);
82
+ rb_define_module_function(rb_mListener, "cone=", Listener_set_cone, 1);
83
+ }
@@ -0,0 +1,8 @@
1
+ #ifndef SFML_RB_AUDIO_LISTENER_H
2
+ #define SFML_RB_AUDIO_LISTENER_H
3
+
4
+ #include <ruby.h>
5
+
6
+ void Init_Listener(VALUE rb_module);
7
+
8
+ #endif //SFML_RB_AUDIO_LISTENER_H
data/ext/audio/music.c ADDED
@@ -0,0 +1,174 @@
1
+ #include "audio/music.h"
2
+
3
+ #include <ruby.h>
4
+ #include <ruby/thread.h>
5
+ #include <stdlib.h>
6
+
7
+ #include "audio/audio_enums.h"
8
+ #include "audio/effect_processor.h"
9
+ #include "audio/sound_source.h"
10
+ #include "core/exceptions.h"
11
+ #include "system/input_stream.h"
12
+ #include "system/time.h"
13
+
14
+ typedef struct {
15
+ SoundSource source;
16
+ /* Music streams from the InputStream lazily, so unlike Font/Image/Shader
17
+ the stream has to outlive the create call. The VALUE is both marked and
18
+ registered as a GC root: the root is what guarantees the InputStream's
19
+ sfInputStream is still alive when sfMusic_destroy runs in dfree, even if
20
+ the Music and the stream become unreachable in the same GC cycle. */
21
+ VALUE rb_stream;
22
+ } Music;
23
+
24
+ static VALUE rb_cMusic;
25
+
26
+ static void Music_mark(void* ptr) {
27
+ Music* music = ptr;
28
+
29
+ rb_gc_mark(music->rb_stream);
30
+ }
31
+
32
+ /* sfMusic_destroy() calls Music::~Music(), which explicitly calls stop() and
33
+ can block waiting on the audio thread; release the GVL for the same reason
34
+ as Sound_free/SS_METHOD(stop). */
35
+ static void* Music_destroy_without_gvl(void* handle) {
36
+ sfMusic_destroy(handle);
37
+ return NULL;
38
+ }
39
+
40
+ static void Music_free(void* ptr) {
41
+ Music* music = ptr;
42
+
43
+ effect_processor_release(music->source.effect_slot);
44
+ rb_thread_call_without_gvl(Music_destroy_without_gvl, music->source.handle, RUBY_UBF_IO, NULL);
45
+ rb_gc_unregister_address(&music->rb_stream);
46
+ free(music);
47
+ }
48
+
49
+ static const rb_data_type_t Music_data_type = {
50
+ .wrap_struct_name = "SFML::Music",
51
+ .function = {.dmark = Music_mark, .dfree = Music_free, .dsize = NULL},
52
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY};
53
+
54
+ static VALUE Music_wrap(VALUE klass, sfMusic* handle, VALUE rb_stream) {
55
+ Music* ptr;
56
+
57
+ if (handle == NULL) {
58
+ rb_raise(rb_eRuntimeError, "failed to create music");
59
+ }
60
+
61
+ ptr = malloc(sizeof(Music));
62
+ ptr->source.handle = handle;
63
+ ptr->source.effect_slot = -1;
64
+ ptr->rb_stream = rb_stream;
65
+
66
+ rb_gc_register_address(&ptr->rb_stream);
67
+
68
+ return TypedData_Wrap_Struct(klass, &Music_data_type, ptr);
69
+ }
70
+
71
+ static VALUE Music_from_file(VALUE klass, VALUE rb_path) {
72
+ return Music_wrap(klass, sfMusic_createFromFile(StringValueCStr(rb_path)), Qnil);
73
+ }
74
+
75
+ static VALUE Music_from_memory(VALUE klass, VALUE rb_data) {
76
+ StringValue(rb_data);
77
+
78
+ return Music_wrap(
79
+ klass, sfMusic_createFromMemory(RSTRING_PTR(rb_data), (size_t)RSTRING_LEN(rb_data)), Qnil);
80
+ }
81
+
82
+ static VALUE Music_from_stream(VALUE klass, VALUE rb_stream) {
83
+ VALUE holder = Qnil;
84
+ sfInputStream* stream = input_stream_from_rb(rb_stream, &holder);
85
+
86
+ return Music_wrap(klass, sfMusic_createFromStream(stream), holder);
87
+ }
88
+
89
+ static VALUE Music_duration(VALUE self) {
90
+ return time_to_rb(sfMusic_getDuration(Get_Music_Struct(self)));
91
+ }
92
+
93
+ static VALUE Music_channel_count(VALUE self) {
94
+ return UINT2NUM(sfMusic_getChannelCount(Get_Music_Struct(self)));
95
+ }
96
+
97
+ static VALUE Music_sample_rate(VALUE self) {
98
+ return UINT2NUM(sfMusic_getSampleRate(Get_Music_Struct(self)));
99
+ }
100
+
101
+ static VALUE Music_channel_map(VALUE self) {
102
+ size_t count = 0;
103
+ const sfSoundChannel* map = sfMusic_getChannelMap(Get_Music_Struct(self), &count);
104
+ VALUE rb_array;
105
+ size_t i;
106
+
107
+ if (map == NULL) {
108
+ return rb_ary_new();
109
+ }
110
+
111
+ rb_array = rb_ary_new_capa((long)count);
112
+
113
+ for (i = 0; i < count; i++) {
114
+ rb_ary_push(rb_array, ID2SYM(rb_intern(sound_channel_name(map[i]))));
115
+ }
116
+
117
+ return rb_array;
118
+ }
119
+
120
+ /* Loop points are an offset plus a length, matching sfTimeSpan. Returned as a
121
+ two-element [offset, length] pair of SFML::Time. */
122
+ static VALUE Music_loop_points(VALUE self) {
123
+ sfTimeSpan span = sfMusic_getLoopPoints(Get_Music_Struct(self));
124
+
125
+ return rb_ary_new_from_args(2, time_to_rb(span.offset), time_to_rb(span.length));
126
+ }
127
+
128
+ static VALUE Music_set_loop_points(VALUE self, VALUE rb_span) {
129
+ sfTimeSpan span;
130
+
131
+ if (!RB_TYPE_P(rb_span, T_ARRAY) || RARRAY_LEN(rb_span) != 2) {
132
+ rb_raise(rb_eArgError, "loop points must be a two-element [offset, length] array");
133
+ }
134
+
135
+ span.offset = time_from_rb(rb_ary_entry(rb_span, 0));
136
+ span.length = time_from_rb(rb_ary_entry(rb_span, 1));
137
+
138
+ sfMusic_setLoopPoints(Get_Music_Struct(self), span);
139
+
140
+ return rb_span;
141
+ }
142
+
143
+ #define SS_FN(name) sfMusic_##name
144
+ #define SS_METHOD(name) Music_##name
145
+ #include "audio/sound_source.inc"
146
+ #undef SS_FN
147
+ #undef SS_METHOD
148
+
149
+ void Init_Music(VALUE rb_module) {
150
+ rb_cMusic = rb_define_class_under(rb_module, "Music", rb_cObject);
151
+
152
+ rb_define_singleton_method(rb_cMusic, "from_file", Music_from_file, 1);
153
+ rb_define_singleton_method(rb_cMusic, "from_memory", Music_from_memory, 1);
154
+ rb_define_singleton_method(rb_cMusic, "from_stream", Music_from_stream, 1);
155
+
156
+ rb_define_method(rb_cMusic, "duration", Music_duration, 0);
157
+ rb_define_method(rb_cMusic, "channel_count", Music_channel_count, 0);
158
+ rb_define_method(rb_cMusic, "sample_rate", Music_sample_rate, 0);
159
+ rb_define_method(rb_cMusic, "channel_map", Music_channel_map, 0);
160
+ rb_define_method(rb_cMusic, "loop_points", Music_loop_points, 0);
161
+ rb_define_method(rb_cMusic, "loop_points=", Music_set_loop_points, 1);
162
+
163
+ Music_define_sound_source_methods(rb_cMusic);
164
+ }
165
+
166
+ VALUE Get_Klass_Music(void) {
167
+ return rb_cMusic;
168
+ }
169
+
170
+ void* Get_Music_Struct(VALUE self) {
171
+ Music* ptr;
172
+ TypedData_Get_Struct(self, Music, &Music_data_type, ptr);
173
+ return ptr->source.handle;
174
+ }
data/ext/audio/music.h ADDED
@@ -0,0 +1,14 @@
1
+ #ifndef SFML_RB_AUDIO_MUSIC_H
2
+ #define SFML_RB_AUDIO_MUSIC_H
3
+
4
+ #include <ruby.h>
5
+
6
+ #include "core/sfml.h"
7
+
8
+ void Init_Music(VALUE rb_module);
9
+
10
+ VALUE Get_Klass_Music(void);
11
+
12
+ void *Get_Music_Struct(VALUE self);
13
+
14
+ #endif //SFML_RB_AUDIO_MUSIC_H