rubygame 2.3.0-x86-mswin32-60
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.
- data/CREDITS +60 -0
- data/LICENSE +504 -0
- data/NEWS +252 -0
- data/README +123 -0
- data/ROADMAP +109 -0
- data/Rakefile +440 -0
- data/doc/extended_readme.rdoc +49 -0
- data/doc/getting_started.rdoc +47 -0
- data/doc/macosx_install.rdoc +70 -0
- data/doc/windows_install.rdoc +123 -0
- data/ext/rubygame/rubygame_core.so +0 -0
- data/ext/rubygame/rubygame_event.c +644 -0
- data/ext/rubygame/rubygame_event.h +48 -0
- data/ext/rubygame/rubygame_event.obj +0 -0
- data/ext/rubygame/rubygame_gfx.c +942 -0
- data/ext/rubygame/rubygame_gfx.h +101 -0
- data/ext/rubygame/rubygame_gfx.obj +0 -0
- data/ext/rubygame/rubygame_gfx.so +0 -0
- data/ext/rubygame/rubygame_gl.c +154 -0
- data/ext/rubygame/rubygame_gl.h +32 -0
- data/ext/rubygame/rubygame_gl.obj +0 -0
- data/ext/rubygame/rubygame_image.c +252 -0
- data/ext/rubygame/rubygame_image.h +41 -0
- data/ext/rubygame/rubygame_image.obj +0 -0
- data/ext/rubygame/rubygame_image.so +0 -0
- data/ext/rubygame/rubygame_joystick.c +247 -0
- data/ext/rubygame/rubygame_joystick.h +41 -0
- data/ext/rubygame/rubygame_joystick.obj +0 -0
- data/ext/rubygame/rubygame_main.c +155 -0
- data/ext/rubygame/rubygame_main.h +36 -0
- data/ext/rubygame/rubygame_main.obj +0 -0
- data/ext/rubygame/rubygame_mixer.c +1024 -0
- data/ext/rubygame/rubygame_mixer.h +36 -0
- data/ext/rubygame/rubygame_mixer.obj +0 -0
- data/ext/rubygame/rubygame_mixer.so +0 -0
- data/ext/rubygame/rubygame_music.c +1017 -0
- data/ext/rubygame/rubygame_music.h +29 -0
- data/ext/rubygame/rubygame_music.obj +0 -0
- data/ext/rubygame/rubygame_screen.c +448 -0
- data/ext/rubygame/rubygame_screen.h +43 -0
- data/ext/rubygame/rubygame_screen.obj +0 -0
- data/ext/rubygame/rubygame_shared.c +272 -0
- data/ext/rubygame/rubygame_shared.h +68 -0
- data/ext/rubygame/rubygame_shared.obj +0 -0
- data/ext/rubygame/rubygame_sound.c +863 -0
- data/ext/rubygame/rubygame_sound.h +29 -0
- data/ext/rubygame/rubygame_sound.obj +0 -0
- data/ext/rubygame/rubygame_surface.c +1151 -0
- data/ext/rubygame/rubygame_surface.h +62 -0
- data/ext/rubygame/rubygame_surface.obj +0 -0
- data/ext/rubygame/rubygame_time.c +183 -0
- data/ext/rubygame/rubygame_time.h +32 -0
- data/ext/rubygame/rubygame_time.obj +0 -0
- data/ext/rubygame/rubygame_ttf.c +599 -0
- data/ext/rubygame/rubygame_ttf.h +69 -0
- data/ext/rubygame/rubygame_ttf.obj +0 -0
- data/ext/rubygame/rubygame_ttf.so +0 -0
- data/lib/rubygame.rb +41 -0
- data/lib/rubygame/clock.rb +128 -0
- data/lib/rubygame/color.rb +79 -0
- data/lib/rubygame/color/models/base.rb +111 -0
- data/lib/rubygame/color/models/hsl.rb +153 -0
- data/lib/rubygame/color/models/hsv.rb +149 -0
- data/lib/rubygame/color/models/rgb.rb +78 -0
- data/lib/rubygame/color/palettes/css.rb +49 -0
- data/lib/rubygame/color/palettes/palette.rb +100 -0
- data/lib/rubygame/color/palettes/x11.rb +177 -0
- data/lib/rubygame/constants.rb +238 -0
- data/lib/rubygame/event.rb +313 -0
- data/lib/rubygame/ftor.rb +370 -0
- data/lib/rubygame/hotspot.rb +265 -0
- data/lib/rubygame/keyconstants.rb +237 -0
- data/lib/rubygame/mediabag.rb +94 -0
- data/lib/rubygame/named_resource.rb +254 -0
- data/lib/rubygame/queue.rb +288 -0
- data/lib/rubygame/rect.rb +612 -0
- data/lib/rubygame/sfont.rb +223 -0
- data/lib/rubygame/sprite.rb +511 -0
- data/samples/FreeSans.ttf +0 -0
- data/samples/GPL.txt +340 -0
- data/samples/README +40 -0
- data/samples/chimp.bmp +0 -0
- data/samples/chimp.rb +302 -0
- data/samples/demo_gl.rb +151 -0
- data/samples/demo_gl_tex.rb +197 -0
- data/samples/demo_music.rb +77 -0
- data/samples/demo_rubygame.rb +296 -0
- data/samples/demo_sfont.rb +52 -0
- data/samples/demo_ttf.rb +193 -0
- data/samples/demo_utf8.rb +53 -0
- data/samples/fist.bmp +0 -0
- data/samples/load_and_blit.rb +22 -0
- data/samples/panda.png +0 -0
- data/samples/punch.wav +0 -0
- data/samples/ruby.png +0 -0
- data/samples/song.ogg +0 -0
- data/samples/term16.png +0 -0
- data/samples/whiff.wav +0 -0
- metadata +164 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
/*
|
2
|
+
* Rubygame -- Ruby code and bindings to SDL to facilitate game creation
|
3
|
+
* Copyright (C) 2004-2007 John Croisant
|
4
|
+
*
|
5
|
+
* This library is free software; you can redistribute it and/or
|
6
|
+
* modify it under the terms of the GNU Lesser General Public
|
7
|
+
* License as published by the Free Software Foundation; either
|
8
|
+
* version 2.1 of the License, or (at your option) any later version.
|
9
|
+
*
|
10
|
+
* This library is distributed in the hope that it will be useful,
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
* Lesser General Public License for more details.
|
14
|
+
*
|
15
|
+
* You should have received a copy of the GNU Lesser General Public
|
16
|
+
* License along with this library; if not, write to the Free Software
|
17
|
+
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
18
|
+
*
|
19
|
+
*/
|
20
|
+
|
21
|
+
#ifndef _RUBYGAME_SCREEN_H
|
22
|
+
#define _RUBYGAME_SCREEN_H
|
23
|
+
|
24
|
+
extern void Rubygame_Init_Screen();
|
25
|
+
|
26
|
+
extern VALUE cScreen;
|
27
|
+
|
28
|
+
extern VALUE rbgm_screen_setmode(int, VALUE*, VALUE);
|
29
|
+
extern VALUE rbgm_screen_getsurface(VALUE);
|
30
|
+
|
31
|
+
extern VALUE rbgm_screen_getcaption(VALUE);
|
32
|
+
extern VALUE rbgm_screen_setcaption(VALUE, VALUE);
|
33
|
+
|
34
|
+
extern VALUE rbgm_screen_seticon(VALUE, VALUE);
|
35
|
+
|
36
|
+
extern VALUE rbgm_screen_update(int, VALUE*, VALUE);
|
37
|
+
extern VALUE rbgm_screen_updaterects(VALUE, VALUE);
|
38
|
+
extern VALUE rbgm_screen_flip(VALUE);
|
39
|
+
|
40
|
+
extern VALUE rbgm_screen_getshowcursor(VALUE);
|
41
|
+
extern VALUE rbgm_screen_setshowcursor(VALUE, VALUE);
|
42
|
+
|
43
|
+
#endif
|
Binary file
|
@@ -0,0 +1,272 @@
|
|
1
|
+
/*
|
2
|
+
* Code that is common to all Rubygame modules.
|
3
|
+
*--
|
4
|
+
* Rubygame -- Ruby code and bindings to SDL to facilitate game creation
|
5
|
+
* Copyright (C) 2004-2007 John Croisant
|
6
|
+
*
|
7
|
+
* This library is free software; you can redistribute it and/or
|
8
|
+
* modify it under the terms of the GNU Lesser General Public
|
9
|
+
* License as published by the Free Software Foundation; either
|
10
|
+
* version 2.1 of the License, or (at your option) any later version.
|
11
|
+
*
|
12
|
+
* This library is distributed in the hope that it will be useful,
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
* Lesser General Public License for more details.
|
16
|
+
*
|
17
|
+
* You should have received a copy of the GNU Lesser General Public
|
18
|
+
* License along with this library; if not, write to the Free Software
|
19
|
+
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
20
|
+
*++
|
21
|
+
*/
|
22
|
+
|
23
|
+
#include "rubygame_shared.h"
|
24
|
+
|
25
|
+
VALUE mRubygame;
|
26
|
+
VALUE cSurface;
|
27
|
+
VALUE cRect;
|
28
|
+
VALUE eSDLError;
|
29
|
+
VALUE mNamedResource;
|
30
|
+
|
31
|
+
|
32
|
+
SDL_Rect *make_rect(int, int, int, int);
|
33
|
+
SDL_Color make_sdl_color(VALUE);
|
34
|
+
int init_video_system();
|
35
|
+
void Init_rubygame_shared();
|
36
|
+
|
37
|
+
SDL_Rect *make_rect(int x, int y, int w, int h)
|
38
|
+
{
|
39
|
+
SDL_Rect *rect;
|
40
|
+
rect = (SDL_Rect *) malloc(sizeof(SDL_Rect));
|
41
|
+
rect->x = x;
|
42
|
+
rect->y = y;
|
43
|
+
rect->w = w;
|
44
|
+
rect->h = h;
|
45
|
+
return rect;
|
46
|
+
}
|
47
|
+
|
48
|
+
/* Returns a symbol from the given char* string */
|
49
|
+
VALUE make_symbol(char *string)
|
50
|
+
{
|
51
|
+
return ID2SYM(rb_intern(string));
|
52
|
+
}
|
53
|
+
|
54
|
+
/* Returns a char* string from the given symbol */
|
55
|
+
char *unmake_symbol(VALUE symbol)
|
56
|
+
{
|
57
|
+
return rb_id2name( SYM2ID(symbol) );
|
58
|
+
}
|
59
|
+
|
60
|
+
|
61
|
+
/* Lowercase, change spaces to underscores, and convert to symbol.
|
62
|
+
* Equivalent to: str.downcase!.gsub!(" ","_").intern
|
63
|
+
*/
|
64
|
+
VALUE sanitized_symbol(char *string)
|
65
|
+
{
|
66
|
+
VALUE str = rb_str_new2(string);
|
67
|
+
|
68
|
+
rb_funcall( str, rb_intern("downcase!"), 0 );
|
69
|
+
rb_funcall( str, rb_intern("gsub!"), 2, rb_str_new2(" "), rb_str_new2("_") );
|
70
|
+
return rb_funcall( str, rb_intern("intern"), 0 );
|
71
|
+
}
|
72
|
+
|
73
|
+
/* Take either nil, Numeric or an Array of Numerics, returns Uint32. */
|
74
|
+
Uint32 collapse_flags(VALUE vflags)
|
75
|
+
{
|
76
|
+
Uint32 flags = 0;
|
77
|
+
int i;
|
78
|
+
|
79
|
+
if( RTEST(vflags) )
|
80
|
+
{
|
81
|
+
switch( TYPE(vflags) ){
|
82
|
+
case T_ARRAY: {
|
83
|
+
int len = RARRAY(vflags)->len;
|
84
|
+
for(i=0; i < len; i++)
|
85
|
+
{
|
86
|
+
flags |= NUM2UINT( rb_ary_entry( vflags,i ) );
|
87
|
+
}
|
88
|
+
break;
|
89
|
+
}
|
90
|
+
case T_BIGNUM: {
|
91
|
+
flags = rb_big2uint( vflags );
|
92
|
+
break;
|
93
|
+
}
|
94
|
+
case T_FIXNUM: {
|
95
|
+
flags = NUM2UINT( vflags );
|
96
|
+
break;
|
97
|
+
}
|
98
|
+
default: {
|
99
|
+
rb_raise(rb_eArgError,"Wrong type for argument `flags' (wanted Number or Array).");
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
return flags;
|
105
|
+
}
|
106
|
+
|
107
|
+
VALUE convert_to_array(VALUE val)
|
108
|
+
{
|
109
|
+
VALUE v = rb_check_array_type(val);
|
110
|
+
if( TYPE(v) != T_ARRAY )
|
111
|
+
{
|
112
|
+
rb_raise(rb_eTypeError, "can't convert %s into Array",
|
113
|
+
rb_obj_classname(val));
|
114
|
+
}
|
115
|
+
return v;
|
116
|
+
}
|
117
|
+
|
118
|
+
/* Takes a Color, Array, or color name (Symbol or String).
|
119
|
+
* Returns an RGBA Array, or raises eTypeError if it can't.
|
120
|
+
*/
|
121
|
+
VALUE convert_color(VALUE color)
|
122
|
+
{
|
123
|
+
if( rb_respond_to(color, rb_intern("to_sdl_rgba_ary")) )
|
124
|
+
{
|
125
|
+
return rb_funcall( color, rb_intern("to_sdl_rgba_ary"), 0 );
|
126
|
+
}
|
127
|
+
else if( rb_respond_to(color, rb_intern("to_ary")) )
|
128
|
+
{
|
129
|
+
return convert_to_array( color );
|
130
|
+
}
|
131
|
+
else if( TYPE(color) == T_SYMBOL || TYPE(color) == T_STRING )
|
132
|
+
{
|
133
|
+
VALUE mColor = rb_const_get( mRubygame, rb_intern("Color") );
|
134
|
+
return convert_color( rb_funcall( mColor, rb_intern("[]"), 1, color) );
|
135
|
+
}
|
136
|
+
else
|
137
|
+
{
|
138
|
+
rb_raise(rb_eTypeError, "unsupported type %s for color",
|
139
|
+
rb_obj_classname(color));
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
SDL_Color make_sdl_color(VALUE vcolor)
|
144
|
+
{
|
145
|
+
SDL_Color color;
|
146
|
+
vcolor = convert_color(vcolor);
|
147
|
+
extract_rgb_u8_as_u8(vcolor, &(color.r), &(color.g), &(color.b));
|
148
|
+
return color;
|
149
|
+
}
|
150
|
+
|
151
|
+
void extract_rgb_u8_as_u8(VALUE color, Uint8 *r, Uint8 *g, Uint8 *b)
|
152
|
+
{
|
153
|
+
*r = NUM2UINT(rb_ary_entry(color, 0));
|
154
|
+
*g = NUM2UINT(rb_ary_entry(color, 1));
|
155
|
+
*b = NUM2UINT(rb_ary_entry(color, 2));
|
156
|
+
}
|
157
|
+
|
158
|
+
void extract_rgba_u8_as_u8(VALUE color, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a)
|
159
|
+
{
|
160
|
+
*r = NUM2UINT(rb_ary_entry(color, 0));
|
161
|
+
*g = NUM2UINT(rb_ary_entry(color, 1));
|
162
|
+
*b = NUM2UINT(rb_ary_entry(color, 2));
|
163
|
+
|
164
|
+
if( RARRAY(color)->len > 3 )
|
165
|
+
{
|
166
|
+
*a = NUM2UINT(rb_ary_entry(color, 3));
|
167
|
+
}
|
168
|
+
else
|
169
|
+
{
|
170
|
+
*a = 255;
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
|
175
|
+
|
176
|
+
/* --
|
177
|
+
*
|
178
|
+
* Issues a deprecation warning for the given feature/method.
|
179
|
+
*
|
180
|
+
* ++
|
181
|
+
*/
|
182
|
+
void rg_deprecated( char *feature, char *version )
|
183
|
+
{
|
184
|
+
rb_warning( "%s is DEPRECATED and will be removed in Rubygame %s! "
|
185
|
+
"Please see the docs for more information.",
|
186
|
+
feature, version );
|
187
|
+
}
|
188
|
+
|
189
|
+
|
190
|
+
|
191
|
+
/* --
|
192
|
+
*
|
193
|
+
* call-seq:
|
194
|
+
* init_video_system() -> int
|
195
|
+
*
|
196
|
+
* Initialize SDL's video subsystem.
|
197
|
+
* Return 0 (zero) on success, non-zero on failure.
|
198
|
+
*
|
199
|
+
* If it has already been initialized, return 0 immediately.
|
200
|
+
*
|
201
|
+
* ++
|
202
|
+
*/
|
203
|
+
int init_video_system()
|
204
|
+
{
|
205
|
+
if( SDL_WasInit(SDL_INIT_VIDEO) == 0 )
|
206
|
+
{
|
207
|
+
return SDL_Init(SDL_INIT_VIDEO);
|
208
|
+
}
|
209
|
+
else
|
210
|
+
{
|
211
|
+
return 0;
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
/* --
|
216
|
+
*
|
217
|
+
* Includes the Rubygame::NamedResource mixin in the given class
|
218
|
+
* and performs the `included' callback.
|
219
|
+
*
|
220
|
+
* ++
|
221
|
+
*/
|
222
|
+
void rg_include_named_resource( VALUE klass )
|
223
|
+
{
|
224
|
+
/* Include the mixin, and manually perform the 'included' callback. */
|
225
|
+
rb_include_module( klass, mNamedResource );
|
226
|
+
rb_funcall( mNamedResource, rb_intern("included"), 1, klass );
|
227
|
+
}
|
228
|
+
|
229
|
+
|
230
|
+
void Init_rubygame_shared()
|
231
|
+
{
|
232
|
+
|
233
|
+
mRubygame = rb_define_module("Rubygame");
|
234
|
+
|
235
|
+
/* Rubygame::Surface class */
|
236
|
+
if( !rb_const_defined(mRubygame,rb_intern("Surface")) )
|
237
|
+
{
|
238
|
+
cSurface = rb_define_class_under(mRubygame,"Surface",rb_cObject);
|
239
|
+
}
|
240
|
+
else
|
241
|
+
{
|
242
|
+
cSurface = rb_const_get(mRubygame,rb_intern("Surface"));
|
243
|
+
}
|
244
|
+
|
245
|
+
/* Rubygame::SDLError class */
|
246
|
+
if( !rb_const_defined(mRubygame,rb_intern("SDLError")))
|
247
|
+
{
|
248
|
+
/* Indicates that an SDL function did not execute properly. */
|
249
|
+
eSDLError = rb_define_class_under(mRubygame,"SDLError",rb_eStandardError);
|
250
|
+
}
|
251
|
+
else
|
252
|
+
{
|
253
|
+
eSDLError = rb_const_get(mRubygame,rb_intern("SDLError"));
|
254
|
+
}
|
255
|
+
|
256
|
+
/* Rubygame::VERSIONS hash table */
|
257
|
+
if( !rb_const_defined(mRubygame, rb_intern("VERSIONS")))
|
258
|
+
{
|
259
|
+
/* A Hash containing the version s of rubygame and it's
|
260
|
+
* compile-time dependencies. */
|
261
|
+
rb_define_const(mRubygame,"VERSIONS",rb_hash_new());
|
262
|
+
}
|
263
|
+
|
264
|
+
|
265
|
+
/* Rubygame::NamedResource mixin. See named_resource.rb. */
|
266
|
+
if( mNamedResource == (int)NULL )
|
267
|
+
{
|
268
|
+
rb_require("rubygame/named_resource");
|
269
|
+
mNamedResource = rb_const_get(mRubygame, rb_intern("NamedResource"));
|
270
|
+
}
|
271
|
+
|
272
|
+
}
|
@@ -0,0 +1,68 @@
|
|
1
|
+
/*
|
2
|
+
* Rubygame -- Ruby code and bindings to SDL to facilitate game creation
|
3
|
+
* Copyright (C) 2004-2007 John Croisant
|
4
|
+
*
|
5
|
+
* This library is free software; you can redistribute it and/or
|
6
|
+
* modify it under the terms of the GNU Lesser General Public
|
7
|
+
* License as published by the Free Software Foundation; either
|
8
|
+
* version 2.1 of the License, or (at your option) any later version.
|
9
|
+
*
|
10
|
+
* This library is distributed in the hope that it will be useful,
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
* Lesser General Public License for more details.
|
14
|
+
*
|
15
|
+
* You should have received a copy of the GNU Lesser General Public
|
16
|
+
* License along with this library; if not, write to the Free Software
|
17
|
+
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
18
|
+
*
|
19
|
+
*/
|
20
|
+
|
21
|
+
#ifndef _RUBYGAME_SHARED_H
|
22
|
+
#define _RUBYGAME_SHARED_H
|
23
|
+
|
24
|
+
#include <SDL.h>
|
25
|
+
#include <ruby.h>
|
26
|
+
#include <stdio.h>
|
27
|
+
|
28
|
+
/* General */
|
29
|
+
extern VALUE mRubygame;
|
30
|
+
extern VALUE eSDLError;
|
31
|
+
extern VALUE cSurface;
|
32
|
+
extern VALUE cRect;
|
33
|
+
extern VALUE mNamedResource;
|
34
|
+
|
35
|
+
extern SDL_Rect *make_rect(int, int, int, int);
|
36
|
+
extern VALUE make_symbol(char *);
|
37
|
+
extern char *unmake_symbol(VALUE);
|
38
|
+
extern VALUE sanitized_symbol(char *);
|
39
|
+
extern Uint32 collapse_flags(VALUE);
|
40
|
+
extern VALUE convert_to_array(VALUE);
|
41
|
+
|
42
|
+
extern SDL_Color make_sdl_color(VALUE);
|
43
|
+
extern void extract_rgb_u8_as_u8(VALUE, Uint8*, Uint8*, Uint8*);
|
44
|
+
extern void extract_rgba_u8_as_u8(VALUE, Uint8*, Uint8*, Uint8*, Uint8*);
|
45
|
+
|
46
|
+
extern void rg_deprecated( char *feature, char *version );
|
47
|
+
|
48
|
+
/* Properly includes the Rubygame::NamedResource mixin in the klass */
|
49
|
+
extern void rg_include_named_resource( VALUE klass );
|
50
|
+
|
51
|
+
extern int init_video_system();
|
52
|
+
extern void Init_rubygame_shared();
|
53
|
+
|
54
|
+
/* Apparently it is not desirable to define these functions when
|
55
|
+
* using Micrsoft Visual C.
|
56
|
+
*/
|
57
|
+
#ifndef _MSC_VER
|
58
|
+
|
59
|
+
static inline int max(int a, int b) {
|
60
|
+
return a > b ? a : b;
|
61
|
+
}
|
62
|
+
static inline int min(int a, int b) {
|
63
|
+
return a > b ? b : a;
|
64
|
+
}
|
65
|
+
|
66
|
+
#endif
|
67
|
+
|
68
|
+
#endif
|
Binary file
|
@@ -0,0 +1,863 @@
|
|
1
|
+
/*
|
2
|
+
* Interface to SDL_mixer sound playback and mixing.
|
3
|
+
*--
|
4
|
+
* Rubygame -- Ruby code and bindings to SDL to facilitate game creation
|
5
|
+
* Copyright (C) 2004-2008 John Croisant
|
6
|
+
*
|
7
|
+
* This library is free software; you can redistribute it and/or
|
8
|
+
* modify it under the terms of the GNU Lesser General Public
|
9
|
+
* License as published by the Free Software Foundation; either
|
10
|
+
* version 2.1 of the License, or (at your option) any later version.
|
11
|
+
*
|
12
|
+
* This library is distributed in the hope that it will be useful,
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
* Lesser General Public License for more details.
|
16
|
+
*
|
17
|
+
* You should have received a copy of the GNU Lesser General Public
|
18
|
+
* License along with this library; if not, write to the Free Software
|
19
|
+
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
20
|
+
*++
|
21
|
+
*/
|
22
|
+
|
23
|
+
#include "SDL_mixer.h"
|
24
|
+
#include "rubygame_shared.h"
|
25
|
+
#include "rubygame_mixer.h"
|
26
|
+
|
27
|
+
VALUE cSound;
|
28
|
+
|
29
|
+
/* A pointer to a Mix_Chunk, with a reference count.
|
30
|
+
* Allows re-use of sound data, then freeing memory when
|
31
|
+
* there are no references to it.
|
32
|
+
*/
|
33
|
+
typedef struct RG_WrapChunk {
|
34
|
+
/* 'private' */
|
35
|
+
Mix_Chunk *chunk;
|
36
|
+
int ref_count;
|
37
|
+
} RG_WrapChunk;
|
38
|
+
|
39
|
+
|
40
|
+
/* The struct that the Sound class wraps. Stores a
|
41
|
+
* pointer to a RG_WrapChunk and important attributes
|
42
|
+
* of the Sound like its volume and channel it's
|
43
|
+
* playing on.
|
44
|
+
*/
|
45
|
+
typedef struct RG_Sound {
|
46
|
+
/* 'public' */
|
47
|
+
float volume;
|
48
|
+
|
49
|
+
/* 'private' */
|
50
|
+
RG_WrapChunk *wrap;
|
51
|
+
int channel;
|
52
|
+
} RG_Sound;
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
/* Allocate/initialize the memory for a RG_WrapChunk and return a pointer. */
|
58
|
+
static RG_WrapChunk* _rg_wrapchunk_alloc()
|
59
|
+
{
|
60
|
+
RG_WrapChunk *wrap;
|
61
|
+
wrap = ALLOC(RG_WrapChunk);
|
62
|
+
|
63
|
+
wrap->chunk = NULL;
|
64
|
+
wrap->ref_count = 0;
|
65
|
+
|
66
|
+
return wrap;
|
67
|
+
}
|
68
|
+
|
69
|
+
/* Load a Mix_Chunk from a file and assign it to the RG_WrapChunk. */
|
70
|
+
static int _rg_wrapchunk_load( RG_WrapChunk *wrap, char *file )
|
71
|
+
{
|
72
|
+
/* Open audio if it's not already. Return -1 if it failed. */
|
73
|
+
if( ensure_open_audio() != 0 )
|
74
|
+
{
|
75
|
+
return -1;
|
76
|
+
}
|
77
|
+
|
78
|
+
wrap->chunk = Mix_LoadWAV( file );
|
79
|
+
|
80
|
+
if( !(wrap->chunk) )
|
81
|
+
return -1;
|
82
|
+
else
|
83
|
+
return 0;
|
84
|
+
}
|
85
|
+
|
86
|
+
/* Make a copy of the other's Mix_Chunk audio data, and assign
|
87
|
+
* it to the RG_WrapChunk.
|
88
|
+
*/
|
89
|
+
static void _rg_wrapchunk_deepcopy( RG_WrapChunk *wrap, RG_WrapChunk *other )
|
90
|
+
{
|
91
|
+
*(wrap->chunk) = *(other->chunk);
|
92
|
+
}
|
93
|
+
|
94
|
+
|
95
|
+
/* Free the memory used by the RG_WrapChunk */
|
96
|
+
static void _rg_wrapchunk_free( RG_WrapChunk *wrap )
|
97
|
+
{
|
98
|
+
Mix_FreeChunk( wrap->chunk );
|
99
|
+
wrap->chunk = NULL;
|
100
|
+
free(wrap);
|
101
|
+
}
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
/* Associate a RG_WrapChunk with a RG_Sound. Handles reference counts. */
|
107
|
+
static inline void _rg_sound_associate( RG_Sound *sound, RG_WrapChunk *wrap )
|
108
|
+
{
|
109
|
+
sound->wrap = wrap;
|
110
|
+
sound->wrap->ref_count += 1;
|
111
|
+
}
|
112
|
+
|
113
|
+
/* Deassociate the RG_Sound's WrapChunk. Handles reference counts. */
|
114
|
+
static inline void _rg_sound_deassociate( RG_Sound *sound )
|
115
|
+
{
|
116
|
+
sound->wrap->ref_count -= 1;
|
117
|
+
sound->wrap = NULL;
|
118
|
+
}
|
119
|
+
|
120
|
+
/* Allocate/initialize the memory for a RG_Sound return a pointer. */
|
121
|
+
static RG_Sound* _rg_sound_alloc()
|
122
|
+
{
|
123
|
+
RG_Sound *sound;
|
124
|
+
sound = ALLOC(RG_Sound);
|
125
|
+
|
126
|
+
sound->wrap = NULL;
|
127
|
+
sound->volume = 1.f;
|
128
|
+
sound->channel = -1;
|
129
|
+
|
130
|
+
return sound;
|
131
|
+
}
|
132
|
+
|
133
|
+
|
134
|
+
/* Free the memory used by the Sound, and possibly the memory
|
135
|
+
* used by the WrapChunk it refers to.
|
136
|
+
*/
|
137
|
+
static void _rg_sound_free( RG_Sound *sound )
|
138
|
+
{
|
139
|
+
RG_WrapChunk *wrap = sound->wrap;
|
140
|
+
|
141
|
+
_rg_sound_deassociate( sound );
|
142
|
+
|
143
|
+
free(sound);
|
144
|
+
|
145
|
+
/* If the WrapChunk has no more referrers, free it too. */
|
146
|
+
if( wrap->ref_count <= 0 )
|
147
|
+
{
|
148
|
+
_rg_wrapchunk_free( wrap );
|
149
|
+
}
|
150
|
+
}
|
151
|
+
|
152
|
+
|
153
|
+
/* Load a new Sound from a file. */
|
154
|
+
static int _rg_sound_load( RG_Sound *sound, char *file )
|
155
|
+
{
|
156
|
+
RG_WrapChunk *wrap = _rg_wrapchunk_alloc();
|
157
|
+
|
158
|
+
int result = _rg_wrapchunk_load( wrap, file );
|
159
|
+
|
160
|
+
_rg_sound_associate( sound, wrap );
|
161
|
+
|
162
|
+
return result;
|
163
|
+
}
|
164
|
+
|
165
|
+
|
166
|
+
/* Make a shallow copy of the given Sound; the new Sound points to
|
167
|
+
* the same audio data in memory as the old one. Also copies
|
168
|
+
* user-visible attributes (e.g. volume).
|
169
|
+
*/
|
170
|
+
static void _rg_sound_copy( RG_Sound *sound, RG_Sound *other )
|
171
|
+
{
|
172
|
+
_rg_sound_associate( sound, other->wrap );
|
173
|
+
|
174
|
+
sound->volume = other->volume;
|
175
|
+
sound->channel = -1;
|
176
|
+
}
|
177
|
+
|
178
|
+
|
179
|
+
/* Make a new Sound with a copy of the audio from an existing Sound */
|
180
|
+
static void _rg_sound_deepcopy( RG_Sound *sound, RG_Sound *other )
|
181
|
+
{
|
182
|
+
RG_WrapChunk *wrap = _rg_wrapchunk_alloc();
|
183
|
+
_rg_wrapchunk_deepcopy( wrap, other->wrap );
|
184
|
+
|
185
|
+
_rg_sound_associate( sound, wrap );
|
186
|
+
|
187
|
+
sound->volume = other->volume;
|
188
|
+
sound->channel = -1;
|
189
|
+
}
|
190
|
+
|
191
|
+
|
192
|
+
/* Check that the given channel is (still) loaded with the given chunk. */
|
193
|
+
static int _rg_sound_channel_check( RG_Sound *sound )
|
194
|
+
{
|
195
|
+
/* channel is unset, so it doesn't belong. */
|
196
|
+
if( sound->channel == -1 )
|
197
|
+
{
|
198
|
+
return 0;
|
199
|
+
}
|
200
|
+
|
201
|
+
Mix_Chunk *chan_chunk = Mix_GetChunk(sound->channel);
|
202
|
+
|
203
|
+
/* Check that the channel chunk is the same as the given one */
|
204
|
+
return ( sound->wrap->chunk == chan_chunk );
|
205
|
+
}
|
206
|
+
|
207
|
+
|
208
|
+
/* Play the sound, fading in, repeating, and stopping as specified.
|
209
|
+
* fade_in and stop_after are in milliseconds!
|
210
|
+
*/
|
211
|
+
static int _rg_sound_play( RG_Sound *sound,
|
212
|
+
int fade_in, int repeats, int stop_after )
|
213
|
+
{
|
214
|
+
|
215
|
+
/* Open audio if it's not already. Return -1 if it failed. */
|
216
|
+
if( ensure_open_audio() != 0 )
|
217
|
+
{
|
218
|
+
return -1;
|
219
|
+
}
|
220
|
+
|
221
|
+
/* If it's already playing on a channel, stop it first. */
|
222
|
+
if( _rg_sound_channel_check(sound) )
|
223
|
+
{
|
224
|
+
Mix_HaltChannel( sound->channel );
|
225
|
+
}
|
226
|
+
|
227
|
+
/* Find first available channel */
|
228
|
+
sound->channel = Mix_GroupAvailable(-1);
|
229
|
+
|
230
|
+
if( sound->channel == -1 )
|
231
|
+
{
|
232
|
+
/* No channels were available, so make one more than there are now.
|
233
|
+
* (Mix_AllocateChannels(-1) returns the current number of channels)
|
234
|
+
*/
|
235
|
+
Mix_AllocateChannels( Mix_AllocateChannels(-1) + 1 );
|
236
|
+
|
237
|
+
/* Try again. */
|
238
|
+
sound->channel = Mix_GroupAvailable(-1);
|
239
|
+
}
|
240
|
+
|
241
|
+
/* Set its volume before we play */
|
242
|
+
Mix_Volume( sound->channel, (int)(MIX_MAX_VOLUME * sound->volume) );
|
243
|
+
|
244
|
+
|
245
|
+
if( fade_in <= 0 )
|
246
|
+
{
|
247
|
+
/* Play sound without fading in */
|
248
|
+
return Mix_PlayChannelTimed( sound->channel, sound->wrap->chunk,
|
249
|
+
repeats, stop_after );
|
250
|
+
}
|
251
|
+
else
|
252
|
+
{
|
253
|
+
/* Play sound with fading in */
|
254
|
+
return Mix_FadeInChannelTimed( sound->channel, sound->wrap->chunk,
|
255
|
+
repeats, fade_in, stop_after );
|
256
|
+
}
|
257
|
+
}
|
258
|
+
|
259
|
+
|
260
|
+
/* Ruby allocation function. */
|
261
|
+
static VALUE rg_sound_alloc( VALUE klass )
|
262
|
+
{
|
263
|
+
RG_Sound *sound = _rg_sound_alloc();
|
264
|
+
return Data_Wrap_Struct(klass, 0, _rg_sound_free, sound);
|
265
|
+
}
|
266
|
+
|
267
|
+
|
268
|
+
|
269
|
+
/*
|
270
|
+
* call-seq:
|
271
|
+
* load( filename ) -> sound
|
272
|
+
*
|
273
|
+
* Load the given audio file.
|
274
|
+
* Supported file formats are WAV, AIFF, RIFF, OGG (Ogg Vorbis), and
|
275
|
+
* VOC (SoundBlaster).
|
276
|
+
*
|
277
|
+
* filename:: Full or relative path to the file. (String, required)
|
278
|
+
*
|
279
|
+
* Returns:: The new Sound instance. (Sound)
|
280
|
+
* May raise:: SDLError, if the sound file could not be loaded.
|
281
|
+
*
|
282
|
+
*/
|
283
|
+
static VALUE rg_sound_load( VALUE klass, VALUE filename )
|
284
|
+
{
|
285
|
+
RG_Sound *sound;
|
286
|
+
|
287
|
+
VALUE s = rg_sound_alloc( cSound );
|
288
|
+
|
289
|
+
Data_Get_Struct( s, RG_Sound, sound );
|
290
|
+
|
291
|
+
char *file = StringValuePtr( filename );
|
292
|
+
|
293
|
+
int result = _rg_sound_load( sound, file );
|
294
|
+
|
295
|
+
if( result == -1 )
|
296
|
+
{
|
297
|
+
rb_raise(eSDLError, "Could not load Sound file '%s': %s",
|
298
|
+
file, Mix_GetError());
|
299
|
+
}
|
300
|
+
|
301
|
+
return s;
|
302
|
+
}
|
303
|
+
|
304
|
+
|
305
|
+
/*
|
306
|
+
* call-seq:
|
307
|
+
* Sound.autoload( filename ) -> Surface or nil
|
308
|
+
*
|
309
|
+
* Searches each directory in Sound.autoload_dirs for a file with
|
310
|
+
* the given filename. If it finds that file, loads it and returns
|
311
|
+
* a Sound instance. If it doesn't find the file, returns nil.
|
312
|
+
*
|
313
|
+
* See Rubygame::NamedResource for more information about this
|
314
|
+
* functionality.
|
315
|
+
*
|
316
|
+
*/
|
317
|
+
VALUE rg_sound_autoload( VALUE klass, VALUE namev )
|
318
|
+
{
|
319
|
+
VALUE pathv = rb_funcall( klass, rb_intern("find_file"), 1, namev );
|
320
|
+
|
321
|
+
if( RTEST(pathv) )
|
322
|
+
{
|
323
|
+
return rg_sound_load( klass, pathv );
|
324
|
+
}
|
325
|
+
else
|
326
|
+
{
|
327
|
+
return Qnil;
|
328
|
+
}
|
329
|
+
}
|
330
|
+
|
331
|
+
|
332
|
+
/*
|
333
|
+
*
|
334
|
+
* call-seq:
|
335
|
+
* new
|
336
|
+
*
|
337
|
+
* **NOTE**: Don't use this method. Use Sound.load.
|
338
|
+
*
|
339
|
+
* This method is not implemented. (In the future, it might be
|
340
|
+
* used to create a new Sound with blank audio data.)
|
341
|
+
*
|
342
|
+
* Raises NotImplementedError.
|
343
|
+
*
|
344
|
+
*/
|
345
|
+
static VALUE rg_sound_new( int argc, VALUE *ARGV, VALUE self )
|
346
|
+
{
|
347
|
+
rb_raise(rb_eNotImpError, "Sound.new is not implemented yet. Use Sound.load to load a sound file.");
|
348
|
+
}
|
349
|
+
|
350
|
+
|
351
|
+
/*
|
352
|
+
* call-seq:
|
353
|
+
* clone( other ) -> sound
|
354
|
+
* dup( other ) -> sound
|
355
|
+
*
|
356
|
+
* Create a copy of the given Sound instance. Much more memory-efficient
|
357
|
+
* than using #load to load the sound file again.
|
358
|
+
*
|
359
|
+
* other:: An existing Sound instance. (Sound, required)
|
360
|
+
*
|
361
|
+
* Returns:: The new Sound instance. (Sound)
|
362
|
+
*
|
363
|
+
* **NOTE**: #clone and #dup do slightly different things; #clone will copy
|
364
|
+
* the 'frozen' state of the object, while #dup will create a fresh, un-frozen
|
365
|
+
* object.
|
366
|
+
*
|
367
|
+
*/
|
368
|
+
static VALUE rg_sound_initialize_copy( VALUE self, VALUE other )
|
369
|
+
{
|
370
|
+
RG_Sound *soundA, *soundB;
|
371
|
+
Data_Get_Struct(self, RG_Sound, soundA);
|
372
|
+
Data_Get_Struct(other, RG_Sound, soundB);
|
373
|
+
|
374
|
+
_rg_sound_copy( soundA, soundB );
|
375
|
+
|
376
|
+
return self;
|
377
|
+
}
|
378
|
+
|
379
|
+
|
380
|
+
|
381
|
+
|
382
|
+
/*
|
383
|
+
* call-seq:
|
384
|
+
* play( options={} ) -> self
|
385
|
+
*
|
386
|
+
* Play the Sound, optionally fading in, repeating a certain number of
|
387
|
+
* times (or forever), and/or stopping automatically after a certain time.
|
388
|
+
*
|
389
|
+
* See also #pause and #stop.
|
390
|
+
*
|
391
|
+
* options:: Hash of options, listed below. (Hash, required)
|
392
|
+
*
|
393
|
+
* :fade_in:: Fade in from silence over the given number of seconds.
|
394
|
+
* (Numeric)
|
395
|
+
* :repeats:: Repeat the sound the given number of times, or forever
|
396
|
+
* (or until stopped) if -1. (Integer)
|
397
|
+
* :stop_after:: Automatically stop playing after playing for the given
|
398
|
+
* number of seconds. (Numeric)
|
399
|
+
*
|
400
|
+
* Returns:: The receiver (self).
|
401
|
+
* May raise:: SDLError, if the sound file could not be played.
|
402
|
+
*
|
403
|
+
* **NOTE**: If the sound is already playing (or paused), it will be stopped
|
404
|
+
* and played again from the beginning.
|
405
|
+
*
|
406
|
+
* Example:
|
407
|
+
* # Fade in over 2 seconds, play 4 times (1 + 3 repeats),
|
408
|
+
* # but stop playing after 5 seconds.
|
409
|
+
* sound.play( :fade_in => 2, :repeats => 3, :stop_after => 5 );
|
410
|
+
*
|
411
|
+
*/
|
412
|
+
static VALUE rg_sound_play( int argc, VALUE *argv, VALUE self )
|
413
|
+
{
|
414
|
+
RG_Sound *sound;
|
415
|
+
Data_Get_Struct(self, RG_Sound, sound);
|
416
|
+
|
417
|
+
VALUE options;
|
418
|
+
rb_scan_args(argc, argv, "01", &options);
|
419
|
+
|
420
|
+
int fade_in = 0;
|
421
|
+
int repeats = 0;
|
422
|
+
int stop_after = -1;
|
423
|
+
|
424
|
+
/* If we got some options */
|
425
|
+
if( RTEST(options) )
|
426
|
+
{
|
427
|
+
/* Make sure options is a Hash table */
|
428
|
+
if( TYPE(options) != T_HASH )
|
429
|
+
{
|
430
|
+
rb_raise(rb_eTypeError, "wrong argument type %s (expected Hash)",
|
431
|
+
rb_obj_classname(options));
|
432
|
+
}
|
433
|
+
|
434
|
+
VALUE temp;
|
435
|
+
|
436
|
+
temp = rb_hash_aref(options, make_symbol("fade_in"));
|
437
|
+
if( RTEST(temp) )
|
438
|
+
{
|
439
|
+
fade_in = (int)(1000 * NUM2DBL( temp ));
|
440
|
+
}
|
441
|
+
|
442
|
+
temp = rb_hash_aref(options, make_symbol("repeats"));
|
443
|
+
if( RTEST(temp) )
|
444
|
+
{
|
445
|
+
repeats = NUM2INT(temp);
|
446
|
+
}
|
447
|
+
|
448
|
+
temp = rb_hash_aref(options, make_symbol("stop_after"));
|
449
|
+
if( RTEST(temp) )
|
450
|
+
{
|
451
|
+
stop_after = (int)(1000 * NUM2DBL( temp ));
|
452
|
+
}
|
453
|
+
|
454
|
+
}
|
455
|
+
|
456
|
+
int result = _rg_sound_play( sound, fade_in, repeats, stop_after );
|
457
|
+
|
458
|
+
if( result == -1 )
|
459
|
+
{
|
460
|
+
rb_raise(eSDLError, "Could not play Sound: %s", Mix_GetError());
|
461
|
+
}
|
462
|
+
|
463
|
+
return self;
|
464
|
+
}
|
465
|
+
|
466
|
+
|
467
|
+
/*
|
468
|
+
* call-seq:
|
469
|
+
* playing? -> true or false
|
470
|
+
*
|
471
|
+
* True if the Sound is currently playing (not paused or stopped).
|
472
|
+
* See also #paused? and #stopped?.
|
473
|
+
*
|
474
|
+
*/
|
475
|
+
static VALUE rg_sound_playingp( VALUE self )
|
476
|
+
{
|
477
|
+
RG_Sound *sound;
|
478
|
+
Data_Get_Struct(self, RG_Sound, sound);
|
479
|
+
|
480
|
+
int channel = sound->channel;
|
481
|
+
|
482
|
+
/* Make sure the sound actually belongs to the channel */
|
483
|
+
if( _rg_sound_channel_check(sound) )
|
484
|
+
{
|
485
|
+
/* Return true if it's playing, but not paused. */
|
486
|
+
if( Mix_Playing(channel) && !Mix_Paused(channel) )
|
487
|
+
{
|
488
|
+
return Qtrue;
|
489
|
+
}
|
490
|
+
else
|
491
|
+
{
|
492
|
+
return Qfalse;
|
493
|
+
}
|
494
|
+
}
|
495
|
+
else
|
496
|
+
{
|
497
|
+
return Qfalse;
|
498
|
+
}
|
499
|
+
}
|
500
|
+
|
501
|
+
|
502
|
+
|
503
|
+
/*
|
504
|
+
* call-seq:
|
505
|
+
* pause -> self
|
506
|
+
*
|
507
|
+
* Pause the Sound. Unlike #stop, it can be unpaused later to resume
|
508
|
+
* from where it was paused. See also #unpause and #paused?.
|
509
|
+
*
|
510
|
+
* Returns:: The receiver (self).
|
511
|
+
*
|
512
|
+
* **NOTE**: Does nothing if the sound is not currently playing.
|
513
|
+
*
|
514
|
+
*/
|
515
|
+
static VALUE rg_sound_pause( VALUE self )
|
516
|
+
{
|
517
|
+
RG_Sound *sound;
|
518
|
+
Data_Get_Struct(self, RG_Sound, sound);
|
519
|
+
|
520
|
+
int channel = sound->channel;
|
521
|
+
|
522
|
+
/* Make sure the sound actually belongs to the channel */
|
523
|
+
if( _rg_sound_channel_check(sound) )
|
524
|
+
{
|
525
|
+
Mix_Pause( channel );
|
526
|
+
}
|
527
|
+
|
528
|
+
return self;
|
529
|
+
}
|
530
|
+
|
531
|
+
|
532
|
+
/*
|
533
|
+
* call-seq:
|
534
|
+
* unpause -> self
|
535
|
+
*
|
536
|
+
* Unpause the Sound, if it is currently paused. Resumes from
|
537
|
+
* where it was paused. See also #pause and #paused?.
|
538
|
+
*
|
539
|
+
* Returns:: The receiver (self).
|
540
|
+
*
|
541
|
+
* **NOTE**: Does nothing if the sound is not currently paused.
|
542
|
+
*
|
543
|
+
*/
|
544
|
+
static VALUE rg_sound_unpause( VALUE self )
|
545
|
+
{
|
546
|
+
RG_Sound *sound;
|
547
|
+
Data_Get_Struct(self, RG_Sound, sound);
|
548
|
+
|
549
|
+
int channel = sound->channel;
|
550
|
+
|
551
|
+
/* Make sure the sound actually belongs to the channel */
|
552
|
+
if( _rg_sound_channel_check(sound) )
|
553
|
+
{
|
554
|
+
Mix_Resume( channel );
|
555
|
+
}
|
556
|
+
|
557
|
+
return self;
|
558
|
+
}
|
559
|
+
|
560
|
+
|
561
|
+
/*
|
562
|
+
* call-seq:
|
563
|
+
* paused? -> true or false
|
564
|
+
*
|
565
|
+
* True if the Sound is currently paused (not playing or stopped).
|
566
|
+
* See also #playing? and #stopped?.
|
567
|
+
*
|
568
|
+
*/
|
569
|
+
static VALUE rg_sound_pausedp( VALUE self )
|
570
|
+
{
|
571
|
+
RG_Sound *sound;
|
572
|
+
Data_Get_Struct(self, RG_Sound, sound);
|
573
|
+
|
574
|
+
int channel = sound->channel;
|
575
|
+
|
576
|
+
/* Make sure the sound actually belongs to the channel */
|
577
|
+
if( _rg_sound_channel_check(sound) )
|
578
|
+
{
|
579
|
+
/* Return true if it's "playing" (not stopped), as well as paused. */
|
580
|
+
if( Mix_Playing(channel) && Mix_Paused(channel) )
|
581
|
+
{
|
582
|
+
return Qtrue;
|
583
|
+
}
|
584
|
+
else
|
585
|
+
{
|
586
|
+
return Qfalse;
|
587
|
+
}
|
588
|
+
}
|
589
|
+
else
|
590
|
+
{
|
591
|
+
return Qfalse;
|
592
|
+
}
|
593
|
+
}
|
594
|
+
|
595
|
+
|
596
|
+
|
597
|
+
/*
|
598
|
+
* call-seq:
|
599
|
+
* stop -> self
|
600
|
+
*
|
601
|
+
* Stop the Sound. Unlike #pause, the sound must be played again from
|
602
|
+
* the beginning, it cannot be resumed from it was stopped.
|
603
|
+
*
|
604
|
+
* Returns:: The receiver (self).
|
605
|
+
*
|
606
|
+
* **NOTE**: Does nothing if the sound is not currently playing or paused.
|
607
|
+
*
|
608
|
+
*/
|
609
|
+
static VALUE rg_sound_stop( VALUE self )
|
610
|
+
{
|
611
|
+
RG_Sound *sound;
|
612
|
+
Data_Get_Struct(self, RG_Sound, sound);
|
613
|
+
|
614
|
+
int channel = sound->channel;
|
615
|
+
|
616
|
+
/* Make sure the sound actually belongs to the channel */
|
617
|
+
if( _rg_sound_channel_check(sound) )
|
618
|
+
{
|
619
|
+
Mix_HaltChannel( channel );
|
620
|
+
}
|
621
|
+
|
622
|
+
return self;
|
623
|
+
}
|
624
|
+
|
625
|
+
|
626
|
+
/*
|
627
|
+
* call-seq:
|
628
|
+
* stopped? -> true or false
|
629
|
+
*
|
630
|
+
* True if the Sound is currently stopped (not playing or paused).
|
631
|
+
* See also #playing? and #paused?.
|
632
|
+
*
|
633
|
+
*/
|
634
|
+
static VALUE rg_sound_stoppedp( VALUE self )
|
635
|
+
{
|
636
|
+
RG_Sound *sound;
|
637
|
+
Data_Get_Struct(self, RG_Sound, sound);
|
638
|
+
|
639
|
+
int channel = sound->channel;
|
640
|
+
|
641
|
+
/* Make sure the sound actually belongs to the channel */
|
642
|
+
if( _rg_sound_channel_check(sound) )
|
643
|
+
{
|
644
|
+
/* Return true if it's not playing. */
|
645
|
+
if( !Mix_Playing(channel) )
|
646
|
+
{
|
647
|
+
return Qtrue;
|
648
|
+
}
|
649
|
+
else
|
650
|
+
{
|
651
|
+
return Qfalse;
|
652
|
+
}
|
653
|
+
}
|
654
|
+
else
|
655
|
+
{
|
656
|
+
/* If it's not on a channel... it can't be playing! */
|
657
|
+
return Qtrue;
|
658
|
+
}
|
659
|
+
}
|
660
|
+
|
661
|
+
|
662
|
+
/*
|
663
|
+
* call-seq:
|
664
|
+
* fade_out( fade_time ) -> self
|
665
|
+
*
|
666
|
+
* Fade out to silence over the given number of seconds. Once the sound
|
667
|
+
* is silent, it is automatically stopped.
|
668
|
+
*
|
669
|
+
* Returns:: The receiver (self).
|
670
|
+
*
|
671
|
+
* **NOTE**: If the sound is currently paused, the fade will start,
|
672
|
+
* but you won't be able to hear it happening unless you #unpause during
|
673
|
+
* the fade. Does nothing if the sound is currently stopped.
|
674
|
+
*
|
675
|
+
*/
|
676
|
+
static VALUE rg_sound_fadeout( VALUE self, VALUE fade_time )
|
677
|
+
{
|
678
|
+
RG_Sound *sound;
|
679
|
+
Data_Get_Struct(self, RG_Sound, sound);
|
680
|
+
|
681
|
+
int channel = sound->channel;
|
682
|
+
int fade_ms = (int)(1000 * NUM2DBL(fade_time));
|
683
|
+
|
684
|
+
/* Make sure the sound actually belongs to the channel */
|
685
|
+
if( _rg_sound_channel_check(sound) )
|
686
|
+
{
|
687
|
+
Mix_FadeOutChannel( channel, fade_ms );
|
688
|
+
}
|
689
|
+
|
690
|
+
return self;
|
691
|
+
}
|
692
|
+
|
693
|
+
|
694
|
+
/*
|
695
|
+
* call-seq:
|
696
|
+
* fading?( direction=:either ) -> true or false
|
697
|
+
*
|
698
|
+
* True if the Sound is currently fading in or out.
|
699
|
+
* See also #play and #fade_out.
|
700
|
+
*
|
701
|
+
* direction:: Check if it is fading :in, :out, or :either.
|
702
|
+
* (Symbol, required)
|
703
|
+
*
|
704
|
+
*/
|
705
|
+
static VALUE rg_sound_fadingp( int argc, VALUE *argv, VALUE self )
|
706
|
+
{
|
707
|
+
RG_Sound *sound;
|
708
|
+
Data_Get_Struct(self, RG_Sound, sound);
|
709
|
+
|
710
|
+
VALUE vdirection;
|
711
|
+
rb_scan_args(argc, argv, "01", &vdirection);
|
712
|
+
|
713
|
+
int direction;
|
714
|
+
int channel = sound->channel;
|
715
|
+
|
716
|
+
/* If it's not actually on a channel, return false right away. */
|
717
|
+
if( !(_rg_sound_channel_check(sound)) )
|
718
|
+
{
|
719
|
+
return Qfalse;
|
720
|
+
}
|
721
|
+
|
722
|
+
if( RTEST(vdirection) )
|
723
|
+
{
|
724
|
+
if( make_symbol("in") == vdirection )
|
725
|
+
{
|
726
|
+
return ( (Mix_FadingChannel(channel) == MIX_FADING_IN) ? Qtrue : Qfalse );
|
727
|
+
}
|
728
|
+
|
729
|
+
else if( make_symbol("out") == vdirection )
|
730
|
+
{
|
731
|
+
return ( (Mix_FadingChannel(channel) == MIX_FADING_OUT) ? Qtrue : Qfalse );
|
732
|
+
}
|
733
|
+
|
734
|
+
else if( make_symbol("either") == vdirection )
|
735
|
+
{
|
736
|
+
return ( (Mix_FadingChannel(channel) != MIX_NO_FADING) ? Qtrue : Qfalse );
|
737
|
+
}
|
738
|
+
}
|
739
|
+
|
740
|
+
/* default */
|
741
|
+
return ( (Mix_FadingChannel(channel) != MIX_NO_FADING) ? Qtrue : Qfalse );
|
742
|
+
}
|
743
|
+
|
744
|
+
|
745
|
+
|
746
|
+
/*
|
747
|
+
* call-seq:
|
748
|
+
* volume -> vol
|
749
|
+
*
|
750
|
+
* Return the volume level of the sound.
|
751
|
+
* 0.0 is totally silent, 1.0 is full volume.
|
752
|
+
*
|
753
|
+
* **NOTE**: Ignores fading in or out.
|
754
|
+
*
|
755
|
+
*/
|
756
|
+
static VALUE rg_sound_getvolume( VALUE self )
|
757
|
+
{
|
758
|
+
RG_Sound *sound;
|
759
|
+
Data_Get_Struct(self, RG_Sound, sound);
|
760
|
+
|
761
|
+
return rb_float_new(sound->volume);
|
762
|
+
}
|
763
|
+
|
764
|
+
|
765
|
+
/*
|
766
|
+
* call-seq:
|
767
|
+
* volume = new_vol
|
768
|
+
*
|
769
|
+
* Set the new #volume level of the sound.
|
770
|
+
* 0.0 is totally silent, 1.0 is full volume.
|
771
|
+
*
|
772
|
+
* Volume cannot be set while the sound is fading in or out.
|
773
|
+
* Be sure to check #fading? or rescue from SDLError when
|
774
|
+
* using this method.
|
775
|
+
*
|
776
|
+
* May raise:: SDLError if the sound is fading in or out.
|
777
|
+
*
|
778
|
+
*/
|
779
|
+
static VALUE rg_sound_setvolume( VALUE self, VALUE volume )
|
780
|
+
{
|
781
|
+
RG_Sound *sound;
|
782
|
+
Data_Get_Struct(self, RG_Sound, sound);
|
783
|
+
|
784
|
+
/* Change channel volume if Sound is currently assigned to a channel */
|
785
|
+
if( _rg_sound_channel_check(sound) )
|
786
|
+
{
|
787
|
+
/* But only if it's not fading right now. */
|
788
|
+
if( Mix_FadingChannel(sound->channel) == MIX_NO_FADING )
|
789
|
+
{
|
790
|
+
sound->volume = NUM2DBL(volume);
|
791
|
+
Mix_Volume( sound->channel, (int)(MIX_MAX_VOLUME * sound->volume) );
|
792
|
+
}
|
793
|
+
else
|
794
|
+
{
|
795
|
+
rb_raise(eSDLError, "cannot set Sound volume while fading");
|
796
|
+
}
|
797
|
+
}
|
798
|
+
else
|
799
|
+
{
|
800
|
+
/* Save it for later. */
|
801
|
+
sound->volume = NUM2DBL(volume);
|
802
|
+
}
|
803
|
+
|
804
|
+
return volume;
|
805
|
+
}
|
806
|
+
|
807
|
+
|
808
|
+
void Rubygame_Init_Sound()
|
809
|
+
{
|
810
|
+
#if 0
|
811
|
+
mRubygame = rb_define_module("Rubygame");
|
812
|
+
#endif
|
813
|
+
|
814
|
+
/*
|
815
|
+
* **IMPORTANT**: Sound is only available if Rubygame was compiled
|
816
|
+
* with SDL_mixer support!
|
817
|
+
*
|
818
|
+
* Sound holds a sound effect, loaded from an audio file (see #load for
|
819
|
+
* supported formats).
|
820
|
+
*
|
821
|
+
* Sound can #play, #pause/#unpause, #stop, adjust #volume,
|
822
|
+
* and #fade_out (you can fade in by passing an option to #play).
|
823
|
+
*
|
824
|
+
* Sound can create duplicates (with #dup or #clone) in a memory-efficient
|
825
|
+
* way -- the new Sound instance refers back to the same audio data,
|
826
|
+
* so having 100 duplicates of a sound uses only slightly more memory
|
827
|
+
* than having the first sound. Duplicates can different volume levels,
|
828
|
+
* too!
|
829
|
+
*
|
830
|
+
* Sound includes the Rubygame::NamedResource mixin module, which
|
831
|
+
* can perform autoloading of sounds on demand, among other things.
|
832
|
+
*
|
833
|
+
*/
|
834
|
+
cSound = rb_define_class_under(mRubygame,"Sound",rb_cObject);
|
835
|
+
|
836
|
+
/* Include the Rubygame::NamedResource mixin module. */
|
837
|
+
rg_include_named_resource(cSound);
|
838
|
+
|
839
|
+
rb_define_alloc_func( cSound, rg_sound_alloc );
|
840
|
+
|
841
|
+
rb_define_singleton_method( cSound, "new", rg_sound_new, -1 );
|
842
|
+
rb_define_singleton_method( cSound, "load", rg_sound_load, 1 );
|
843
|
+
|
844
|
+
rb_define_singleton_method( cSound, "autoload", rg_sound_autoload, 1 );
|
845
|
+
|
846
|
+
rb_define_method( cSound, "initialize_copy", rg_sound_initialize_copy, 1 );
|
847
|
+
|
848
|
+
rb_define_method( cSound, "play", rg_sound_play, -1 );
|
849
|
+
rb_define_method( cSound, "playing?", rg_sound_playingp, 0 );
|
850
|
+
|
851
|
+
rb_define_method( cSound, "pause", rg_sound_pause, 0 );
|
852
|
+
rb_define_method( cSound, "unpause", rg_sound_unpause, 0 );
|
853
|
+
rb_define_method( cSound, "paused?", rg_sound_pausedp, 0 );
|
854
|
+
|
855
|
+
rb_define_method( cSound, "stop", rg_sound_stop, 0 );
|
856
|
+
rb_define_method( cSound, "stopped?", rg_sound_stoppedp, 0 );
|
857
|
+
|
858
|
+
rb_define_method( cSound, "fade_out", rg_sound_fadeout, 1 );
|
859
|
+
rb_define_method( cSound, "fading?", rg_sound_fadingp, -1 );
|
860
|
+
|
861
|
+
rb_define_method( cSound, "volume", rg_sound_getvolume, 0 );
|
862
|
+
rb_define_method( cSound, "volume=", rg_sound_setvolume, 1 );
|
863
|
+
}
|