rubygame 2.3.0-x86-linux

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/CREDITS +60 -0
  2. data/LICENSE +504 -0
  3. data/NEWS +252 -0
  4. data/README +123 -0
  5. data/ROADMAP +109 -0
  6. data/Rakefile +438 -0
  7. data/doc/extended_readme.rdoc +49 -0
  8. data/doc/getting_started.rdoc +47 -0
  9. data/doc/macosx_install.rdoc +70 -0
  10. data/doc/windows_install.rdoc +123 -0
  11. data/ext/rubygame/rubygame_core.so +0 -0
  12. data/ext/rubygame/rubygame_event.c +644 -0
  13. data/ext/rubygame/rubygame_event.h +48 -0
  14. data/ext/rubygame/rubygame_event.o +0 -0
  15. data/ext/rubygame/rubygame_gfx.c +942 -0
  16. data/ext/rubygame/rubygame_gfx.h +101 -0
  17. data/ext/rubygame/rubygame_gfx.o +0 -0
  18. data/ext/rubygame/rubygame_gfx.so +0 -0
  19. data/ext/rubygame/rubygame_gl.c +154 -0
  20. data/ext/rubygame/rubygame_gl.h +32 -0
  21. data/ext/rubygame/rubygame_gl.o +0 -0
  22. data/ext/rubygame/rubygame_image.c +252 -0
  23. data/ext/rubygame/rubygame_image.h +41 -0
  24. data/ext/rubygame/rubygame_image.o +0 -0
  25. data/ext/rubygame/rubygame_image.so +0 -0
  26. data/ext/rubygame/rubygame_joystick.c +247 -0
  27. data/ext/rubygame/rubygame_joystick.h +41 -0
  28. data/ext/rubygame/rubygame_joystick.o +0 -0
  29. data/ext/rubygame/rubygame_main.c +155 -0
  30. data/ext/rubygame/rubygame_main.h +36 -0
  31. data/ext/rubygame/rubygame_main.o +0 -0
  32. data/ext/rubygame/rubygame_mixer.c +1024 -0
  33. data/ext/rubygame/rubygame_mixer.h +36 -0
  34. data/ext/rubygame/rubygame_mixer.o +0 -0
  35. data/ext/rubygame/rubygame_mixer.so +0 -0
  36. data/ext/rubygame/rubygame_music.c +1017 -0
  37. data/ext/rubygame/rubygame_music.h +29 -0
  38. data/ext/rubygame/rubygame_music.o +0 -0
  39. data/ext/rubygame/rubygame_screen.c +448 -0
  40. data/ext/rubygame/rubygame_screen.h +43 -0
  41. data/ext/rubygame/rubygame_screen.o +0 -0
  42. data/ext/rubygame/rubygame_shared.c +272 -0
  43. data/ext/rubygame/rubygame_shared.h +68 -0
  44. data/ext/rubygame/rubygame_shared.o +0 -0
  45. data/ext/rubygame/rubygame_sound.c +863 -0
  46. data/ext/rubygame/rubygame_sound.h +29 -0
  47. data/ext/rubygame/rubygame_sound.o +0 -0
  48. data/ext/rubygame/rubygame_surface.c +1151 -0
  49. data/ext/rubygame/rubygame_surface.h +62 -0
  50. data/ext/rubygame/rubygame_surface.o +0 -0
  51. data/ext/rubygame/rubygame_time.c +183 -0
  52. data/ext/rubygame/rubygame_time.h +32 -0
  53. data/ext/rubygame/rubygame_time.o +0 -0
  54. data/ext/rubygame/rubygame_ttf.c +599 -0
  55. data/ext/rubygame/rubygame_ttf.h +69 -0
  56. data/ext/rubygame/rubygame_ttf.o +0 -0
  57. data/ext/rubygame/rubygame_ttf.so +0 -0
  58. data/lib/rubygame.rb +41 -0
  59. data/lib/rubygame/clock.rb +128 -0
  60. data/lib/rubygame/color.rb +79 -0
  61. data/lib/rubygame/color/models/base.rb +111 -0
  62. data/lib/rubygame/color/models/hsl.rb +153 -0
  63. data/lib/rubygame/color/models/hsv.rb +149 -0
  64. data/lib/rubygame/color/models/rgb.rb +78 -0
  65. data/lib/rubygame/color/palettes/css.rb +49 -0
  66. data/lib/rubygame/color/palettes/palette.rb +100 -0
  67. data/lib/rubygame/color/palettes/x11.rb +177 -0
  68. data/lib/rubygame/constants.rb +238 -0
  69. data/lib/rubygame/event.rb +313 -0
  70. data/lib/rubygame/ftor.rb +370 -0
  71. data/lib/rubygame/hotspot.rb +265 -0
  72. data/lib/rubygame/keyconstants.rb +237 -0
  73. data/lib/rubygame/mediabag.rb +94 -0
  74. data/lib/rubygame/named_resource.rb +254 -0
  75. data/lib/rubygame/queue.rb +288 -0
  76. data/lib/rubygame/rect.rb +612 -0
  77. data/lib/rubygame/sfont.rb +223 -0
  78. data/lib/rubygame/sprite.rb +511 -0
  79. data/samples/FreeSans.ttf +0 -0
  80. data/samples/GPL.txt +340 -0
  81. data/samples/README +40 -0
  82. data/samples/chimp.bmp +0 -0
  83. data/samples/chimp.rb +302 -0
  84. data/samples/demo_gl.rb +151 -0
  85. data/samples/demo_gl_tex.rb +197 -0
  86. data/samples/demo_music.rb +77 -0
  87. data/samples/demo_rubygame.rb +296 -0
  88. data/samples/demo_sfont.rb +52 -0
  89. data/samples/demo_ttf.rb +193 -0
  90. data/samples/demo_utf8.rb +53 -0
  91. data/samples/fist.bmp +0 -0
  92. data/samples/load_and_blit.rb +22 -0
  93. data/samples/panda.png +0 -0
  94. data/samples/punch.wav +0 -0
  95. data/samples/ruby.png +0 -0
  96. data/samples/song.ogg +0 -0
  97. data/samples/term16.png +0 -0
  98. data/samples/whiff.wav +0 -0
  99. data/test/audio_spec.rb +236 -0
  100. data/test/color_spec.rb +544 -0
  101. data/test/image.png +0 -0
  102. data/test/music_spec.rb +727 -0
  103. data/test/named_resource_spec.rb +211 -0
  104. data/test/short.ogg +0 -0
  105. data/test/sound_spec.rb +564 -0
  106. data/test/surface_spec.rb +219 -0
  107. data/test/test_crop.rb +45 -0
  108. data/test/test_rect.rb +841 -0
  109. metadata +174 -0
@@ -0,0 +1,36 @@
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_MIXER_H
22
+ #define _RUBYGAME_MIXER_H
23
+
24
+ #include "SDL_audio.h"
25
+ #include "SDL_mixer.h"
26
+
27
+ extern int audio_is_open();
28
+ extern int ensure_open_audio();
29
+
30
+ extern void Init_rubygame_mixer();
31
+
32
+ extern VALUE mMixer;
33
+ extern VALUE cSample;
34
+ extern VALUE cOldMusic;
35
+
36
+ #endif
Binary file
Binary file
@@ -0,0 +1,1017 @@
1
+ /*
2
+ * Interface to SDL_mixer music 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 cMusic;
28
+
29
+
30
+ /*
31
+ * A pointer to a Mix_Music, with a reference count.
32
+ * Allows re-use of music data, then freeing memory when
33
+ * there are no references to it.
34
+ */
35
+ typedef struct RG_WrapMusic {
36
+ /* 'private' */
37
+ Mix_Music *music;
38
+ int ref_count;
39
+ } RG_WrapMusic;
40
+
41
+
42
+ /*
43
+ * The struct that the Music class wraps. Stores a
44
+ * pointer to a RG_WrapMusic and important attributes
45
+ * of the Music like its volume.
46
+ */
47
+ typedef struct RG_Music {
48
+ /* 'public' */
49
+ float volume;
50
+
51
+ /* 'private' */
52
+ RG_WrapMusic *wrap;
53
+ int repeats;
54
+ } RG_Music;
55
+
56
+
57
+
58
+
59
+ /* Allocate/initialize the memory for a RG_WrapMusic and return a pointer. */
60
+ static RG_WrapMusic* _rg_wrapmusic_alloc()
61
+ {
62
+ RG_WrapMusic *wrap;
63
+ wrap = ALLOC(RG_WrapMusic);
64
+
65
+ wrap->music = NULL;
66
+ wrap->ref_count = 0;
67
+
68
+ return wrap;
69
+ }
70
+
71
+ /* Load a Mix_Music from a file and assign it to the RG_WrapMusic. */
72
+ static int _rg_wrapmusic_load( RG_WrapMusic *wrap, char *file )
73
+ {
74
+ wrap->music = Mix_LoadMUS( file );
75
+
76
+ if( !(wrap->music) )
77
+ return -1;
78
+ else
79
+ return 0;
80
+ }
81
+
82
+ /* Make a copy of the other's Mix_Music data, and assign
83
+ * it to the RG_WrapMusic.
84
+ */
85
+ static void _rg_wrapmusic_deepcopy( RG_WrapMusic *wrap, RG_WrapMusic *other )
86
+ {
87
+ wrap->music = other->music;
88
+ }
89
+
90
+
91
+ /* Free the memory used by the RG_WrapMusic */
92
+ static void _rg_wrapmusic_free( RG_WrapMusic *wrap )
93
+ {
94
+ Mix_FreeMusic( wrap->music );
95
+ wrap->music = NULL;
96
+ free(wrap);
97
+ }
98
+
99
+
100
+
101
+
102
+ /* Associate a RG_WrapMusic with a RG_Music. Handles reference counts. */
103
+ static inline void _rg_music_associate( RG_Music *music, RG_WrapMusic *wrap )
104
+ {
105
+ music->wrap = wrap;
106
+ music->wrap->ref_count += 1;
107
+ }
108
+
109
+ /* Deassociate the RG_Music's WrapMusic. Handles reference counts. */
110
+ static inline void _rg_music_deassociate( RG_Music *music )
111
+ {
112
+ music->wrap->ref_count -= 1;
113
+ music->wrap = NULL;
114
+ }
115
+
116
+ /* Allocate/initialize the memory for a RG_Music and return a pointer. */
117
+ static RG_Music* _rg_music_alloc()
118
+ {
119
+ RG_Music *music;
120
+ music = ALLOC(RG_Music);
121
+
122
+ music->wrap = NULL;
123
+ music->volume = 1.f;
124
+ music->repeats = 0;
125
+
126
+ return music;
127
+ }
128
+
129
+
130
+ /*
131
+ * Free the memory used by the RG_Music, and possibly the memory
132
+ * used by the WrapMusic it refers to.
133
+ */
134
+ static void _rg_music_free( RG_Music *music )
135
+ {
136
+ RG_WrapMusic *wrap = music->wrap;
137
+
138
+ _rg_music_deassociate( music );
139
+
140
+ free(music);
141
+
142
+ /* If the WrapMusic has no more referrers, free it too. */
143
+ if( wrap->ref_count <= 0 )
144
+ {
145
+ _rg_wrapmusic_free( wrap );
146
+ }
147
+ }
148
+
149
+
150
+ /* Load a new Music from a file. */
151
+ static int _rg_music_load( RG_Music *music, char *file )
152
+ {
153
+ RG_WrapMusic *wrap = _rg_wrapmusic_alloc();
154
+
155
+ int result = _rg_wrapmusic_load( wrap, file );
156
+
157
+ _rg_music_associate( music, wrap );
158
+
159
+ return result;
160
+ }
161
+
162
+
163
+ /*
164
+ * Make a shallow copy of the given Music; the new Music points to
165
+ * the same audio data in memory as the old one. Also copies
166
+ * user-visible attributes (e.g. volume).
167
+ */
168
+ static void _rg_music_copy( RG_Music *music, RG_Music *other )
169
+ {
170
+ _rg_music_associate( music, other->wrap );
171
+
172
+ music->volume = other->volume;
173
+ }
174
+
175
+
176
+ /* Make a new Music with a copy of the audio from an existing Music */
177
+ static void _rg_music_deepcopy( RG_Music *music, RG_Music *other )
178
+ {
179
+ RG_WrapMusic *wrap = _rg_wrapmusic_alloc();
180
+ _rg_wrapmusic_deepcopy( wrap, other->wrap );
181
+
182
+ _rg_music_associate( music, wrap );
183
+
184
+ music->volume = other->volume;
185
+ }
186
+
187
+
188
+ /*
189
+ * Play the music, fading in, repeating, and starting at a time as specified.
190
+ * fade_in and start_at are in milliseconds here!
191
+ */
192
+ static int _rg_music_play( RG_Music *music,
193
+ int fade_in, int repeats, double start_at )
194
+ {
195
+
196
+ /* Open audio if it's not already. Return -1 if it failed. */
197
+ if( ensure_open_audio() != 0 )
198
+ {
199
+ return -1;
200
+ }
201
+
202
+ /* Doing a little restart dance to please the SDL_mixer gods. */
203
+ Mix_PlayMusic( music->wrap->music, 0 );
204
+ Mix_HaltMusic();
205
+
206
+ /* Set music channel volume before we play */
207
+ Mix_VolumeMusic( (int)(MIX_MAX_VOLUME * music->volume) );
208
+
209
+
210
+ if( fade_in <= 0 )
211
+ {
212
+ fade_in = 0;
213
+ }
214
+
215
+ if( start_at < 0 )
216
+ {
217
+ start_at = 0;
218
+ }
219
+
220
+ /* Remember repeats, for when we rewind or jump. */
221
+ music->repeats = repeats;
222
+
223
+ /* Play music as specified, and return whether it worked. */
224
+ return Mix_FadeInMusicPos( music->wrap->music,
225
+ repeats, fade_in, start_at );
226
+
227
+ }
228
+
229
+
230
+
231
+
232
+ /*
233
+ * Return the Music which is currently set on the music channel (i.e.
234
+ * was played most recently) or nil if no Music has ever been played.
235
+ *
236
+ * NOTE: The current Music could be playing, paused, or stopped. Use
237
+ * #playing? or #stopped? if you want to know whether the current
238
+ * music is still playing.
239
+ */
240
+ static VALUE rg_music_current( VALUE klass )
241
+ {
242
+ return rb_iv_get( cMusic, "@current_music" );
243
+ }
244
+
245
+
246
+ /* Set the currently-active Music to the given Music or nil.
247
+ */
248
+ static VALUE _rg_music_set_current( VALUE music )
249
+ {
250
+ return rb_iv_set( cMusic, "@current_music", music );
251
+ }
252
+
253
+
254
+ /* Check that the music is currently active on the music channel. */
255
+ static int _rg_music_current_check( VALUE music )
256
+ {
257
+ return ( rg_music_current( cMusic ) == music );
258
+ }
259
+
260
+
261
+ /* Ruby allocation function. */
262
+ static VALUE rg_music_alloc( VALUE klass )
263
+ {
264
+ RG_Music *music = _rg_music_alloc();
265
+ return Data_Wrap_Struct(klass, 0, _rg_music_free, music);
266
+ }
267
+
268
+
269
+
270
+ /*
271
+ * call-seq:
272
+ * load( filename ) -> music
273
+ *
274
+ * Load the given audio file.
275
+ * Supported file formats are WAVE, MOD, MIDI, OGG, and MP3.
276
+ *
277
+ * filename:: Full or relative path to the file. (String, required)
278
+ *
279
+ * Returns:: The new Music instance. (Music)
280
+ * May raise:: SDLError, if the music file could not be loaded.
281
+ *
282
+ */
283
+ static VALUE rg_music_load( VALUE klass, VALUE filename )
284
+ {
285
+ RG_Music *music;
286
+
287
+ VALUE s = rg_music_alloc( cMusic );
288
+
289
+ Data_Get_Struct( s, RG_Music, music );
290
+
291
+ char *file = StringValuePtr( filename );
292
+
293
+ int result = _rg_music_load( music, file );
294
+
295
+ if( result == -1 )
296
+ {
297
+ rb_raise(eSDLError, "Could not load Music file '%s': %s",
298
+ file, Mix_GetError());
299
+ }
300
+
301
+ return s;
302
+ }
303
+
304
+
305
+ /*
306
+ * call-seq:
307
+ * Music.autoload( filename ) -> Surface or nil
308
+ *
309
+ * Searches each directory in Music.autoload_dirs for a file with
310
+ * the given filename. If it finds that file, loads it and returns
311
+ * a Music 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_music_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_music_load( klass, pathv );
324
+ }
325
+ else
326
+ {
327
+ return Qnil;
328
+ }
329
+ }
330
+
331
+
332
+ /*
333
+ * call-seq:
334
+ * new
335
+ *
336
+ * **NOTE**: Don't use this method. Use Music.load.
337
+ *
338
+ * Raises NotImplementedError.
339
+ *
340
+ */
341
+ static VALUE rg_music_new( int argc, VALUE *ARGV, VALUE self )
342
+ {
343
+ rb_raise(rb_eNotImpError, "Music.new is not implemented. Use Music.load to load a music file.");
344
+ }
345
+
346
+ /*
347
+ * call-seq:
348
+ * clone( other ) -> music
349
+ * dup( other ) -> music
350
+ *
351
+ * Create a copy of the given Music instance. More efficient
352
+ * than using #load to load the music file again.
353
+ *
354
+ * other:: An existing Music instance. (Music, required)
355
+ *
356
+ * Returns:: The new Music instance. (Music)
357
+ *
358
+ * **NOTE**: #clone and #dup do slightly different things; #clone will copy
359
+ * the 'frozen' state of the object, while #dup will create a fresh, un-frozen
360
+ * object.
361
+ *
362
+ */
363
+ static VALUE rg_music_initialize_copy( VALUE self, VALUE other )
364
+ {
365
+ RG_Music *musicA, *musicB;
366
+ Data_Get_Struct(self, RG_Music, musicA);
367
+ Data_Get_Struct(other, RG_Music, musicB);
368
+
369
+ _rg_music_copy( musicA, musicB );
370
+
371
+ return self;
372
+ }
373
+
374
+
375
+
376
+
377
+ /*
378
+ * call-seq:
379
+ * play( options={:fade_in => 0, :repeats => 0, :start_at => 0} ) -> self
380
+ *
381
+ * Play the Music, optionally fading in, repeating a certain number
382
+ * of times (or forever), and/or starting at a certain position in
383
+ * the song.
384
+ *
385
+ * See also #pause and #stop.
386
+ *
387
+ * options:: Hash of options, listed below. (Hash, required)
388
+ *
389
+ * :fade_in:: Fade in from silence over the given number of
390
+ * seconds. Default: 0. (Numeric, optional)
391
+ * :repeats:: Repeat the music the given number of times, or
392
+ * forever (or until stopped) if -1. Default: 0.
393
+ * (Integer, optional)
394
+ * :start_at:: Start playing the music at the given time in the
395
+ * song, in seconds. Default: 0. (Numeric, optional)
396
+ * **NOTE**: Non-zero start times only work for
397
+ * OGG and MP3 formats! Please refer to #jump.
398
+ *
399
+ *
400
+ * Returns:: The receiver (self).
401
+ * May raise:: SDLError, if the music file could not be played, or
402
+ * if you used :start_at with an unsupported format.
403
+ *
404
+ * **NOTE**: Only one music can be playing at once. If any music is
405
+ * already playing (or paused), it will be stopped before playing the
406
+ * new music.
407
+ *
408
+ * Example:
409
+ * # Fade in over 2 seconds, play 4 times (1 + 3 repeats),
410
+ * # starting at 60 seconds since the beginning of the song.
411
+ * music.play( :fade_in => 2, :repeats => 3, :start_at => 60 );
412
+ *
413
+ */
414
+ static VALUE rg_music_play( int argc, VALUE *argv, VALUE self )
415
+ {
416
+ RG_Music *music;
417
+ Data_Get_Struct(self, RG_Music, music);
418
+
419
+ VALUE options;
420
+ rb_scan_args(argc, argv, "01", &options);
421
+
422
+ int fade_in = 0;
423
+ int repeats = 1;
424
+ double start_at = 0;
425
+
426
+ /* If we got some options */
427
+ if( RTEST(options) )
428
+ {
429
+ /* Make sure options is a Hash table */
430
+ if( TYPE(options) != T_HASH )
431
+ {
432
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Hash)",
433
+ rb_obj_classname(options));
434
+ }
435
+
436
+ VALUE temp;
437
+
438
+
439
+ temp = rb_hash_aref(options, make_symbol("fade_in"));
440
+ if( RTEST(temp) )
441
+ {
442
+ fade_in = (int)(1000 * NUM2DBL( temp ));
443
+
444
+ if( fade_in < 0 )
445
+ {
446
+ rb_raise(rb_eArgError, ":fade_in cannot be negative (got %.2f)",
447
+ fade_in / 1000);
448
+ }
449
+ else if( fade_in < 50 )
450
+ {
451
+ /* Work-around for a bug with SDL_mixer not working with small non-zero fade-ins */
452
+ fade_in = 0;
453
+ }
454
+ }
455
+
456
+
457
+ temp = rb_hash_aref(options, make_symbol("repeats"));
458
+ if( RTEST(temp) )
459
+ {
460
+ repeats = NUM2INT(temp);
461
+
462
+ if( repeats > -1 )
463
+ {
464
+ /* Adjust so repeats means the same as it does for Sound */
465
+ repeats += 1;
466
+ }
467
+
468
+ if( repeats < -1 )
469
+ {
470
+ rb_raise(rb_eArgError, ":repeats cannot be negative, except -1 (got %d)",
471
+ repeats);
472
+ }
473
+ }
474
+
475
+
476
+ temp = rb_hash_aref(options, make_symbol("start_at"));
477
+ if( RTEST(temp) )
478
+ {
479
+ start_at = (double)(NUM2DBL( temp ));
480
+
481
+ if( start_at < 0 )
482
+ {
483
+ rb_raise(rb_eArgError, ":start_at cannot be negative (got %.2f)",
484
+ start_at);
485
+ }
486
+ }
487
+
488
+ }
489
+
490
+ int result = _rg_music_play( music, fade_in, repeats, start_at );
491
+
492
+ if( result == -1 )
493
+ {
494
+ rb_raise(eSDLError, "Could not play Music: %s", Mix_GetError());
495
+ }
496
+
497
+ /* This music is now current. */
498
+ _rg_music_set_current( self );
499
+
500
+ return self;
501
+ }
502
+
503
+
504
+ /*
505
+ * call-seq:
506
+ * playing? -> true or false
507
+ *
508
+ * True if the Music is currently playing (not paused or stopped).
509
+ * See also #paused? and #stopped?.
510
+ *
511
+ */
512
+ static VALUE rg_music_playingp( VALUE self )
513
+ {
514
+ RG_Music *music;
515
+ Data_Get_Struct(self, RG_Music, music);
516
+
517
+ /* Check that the music is current. */
518
+ if( _rg_music_current_check(self) )
519
+ {
520
+ /* Return true if music is playing, but not paused. */
521
+ if( Mix_PlayingMusic() && !Mix_PausedMusic() )
522
+ {
523
+ return Qtrue;
524
+ }
525
+ else
526
+ {
527
+ return Qfalse;
528
+ }
529
+ }
530
+ else
531
+ {
532
+ return Qfalse;
533
+ }
534
+ }
535
+
536
+
537
+
538
+ /*
539
+ * call-seq:
540
+ * pause -> self
541
+ *
542
+ * Pause the Music. Unlike #stop, it can be unpaused later to resume
543
+ * from where it was paused. See also #unpause and #paused?.
544
+ *
545
+ * Returns:: The receiver (self).
546
+ *
547
+ * **NOTE**: Does nothing if the music is not currently playing.
548
+ *
549
+ */
550
+ static VALUE rg_music_pause( VALUE self )
551
+ {
552
+ RG_Music *music;
553
+ Data_Get_Struct(self, RG_Music, music);
554
+
555
+ /* Check that the music is current. */
556
+ if( _rg_music_current_check(self) )
557
+ {
558
+ Mix_PauseMusic();
559
+ }
560
+
561
+ return self;
562
+ }
563
+
564
+
565
+ /*
566
+ * call-seq:
567
+ * unpause -> self
568
+ *
569
+ * Unpause the Music, if it is currently paused. Resumes from
570
+ * where it was paused. See also #pause and #paused?.
571
+ *
572
+ * Returns:: The receiver (self).
573
+ *
574
+ * **NOTE**: Does nothing if the music is not currently paused.
575
+ *
576
+ */
577
+ static VALUE rg_music_unpause( VALUE self )
578
+ {
579
+ RG_Music *music;
580
+ Data_Get_Struct(self, RG_Music, music);
581
+
582
+ /* Check that the music is current. */
583
+ if( _rg_music_current_check(self) )
584
+ {
585
+ Mix_ResumeMusic();
586
+ }
587
+
588
+ return self;
589
+ }
590
+
591
+
592
+ /*
593
+ * call-seq:
594
+ * paused? -> true or false
595
+ *
596
+ * True if the Music is currently paused (not playing or stopped).
597
+ * See also #playing? and #stopped?.
598
+ *
599
+ */
600
+ static VALUE rg_music_pausedp( VALUE self )
601
+ {
602
+ RG_Music *music;
603
+ Data_Get_Struct(self, RG_Music, music);
604
+
605
+ /* Check that the music is current. */
606
+ if( _rg_music_current_check(self) )
607
+ {
608
+ /* Return true if it's "playing" (not stopped), as well as paused. */
609
+ if( Mix_PlayingMusic() && Mix_PausedMusic() )
610
+ {
611
+ return Qtrue;
612
+ }
613
+ else
614
+ {
615
+ return Qfalse;
616
+ }
617
+ }
618
+ else
619
+ {
620
+ return Qfalse;
621
+ }
622
+ }
623
+
624
+
625
+
626
+ /*
627
+ * call-seq:
628
+ * stop -> self
629
+ *
630
+ * Stop the Music. Unlike #pause, the music must be played again from
631
+ * the beginning, it cannot be resumed from it was stopped.
632
+ *
633
+ * Returns:: The receiver (self).
634
+ *
635
+ * **NOTE**: Does nothing if the music is not currently playing or paused.
636
+ *
637
+ */
638
+ static VALUE rg_music_stop( VALUE self )
639
+ {
640
+ RG_Music *music;
641
+ Data_Get_Struct(self, RG_Music, music);
642
+
643
+ /* Check that the music is current. */
644
+ if( _rg_music_current_check(self) )
645
+ {
646
+ Mix_HaltMusic();
647
+ }
648
+
649
+ return self;
650
+ }
651
+
652
+
653
+ /*
654
+ * call-seq:
655
+ * stopped? -> true or false
656
+ *
657
+ * True if the Music is currently stopped (not playing or paused).
658
+ * See also #playing? and #paused?.
659
+ *
660
+ */
661
+ static VALUE rg_music_stoppedp( VALUE self )
662
+ {
663
+ RG_Music *music;
664
+ Data_Get_Struct(self, RG_Music, music);
665
+
666
+ /* Check that the music is current. */
667
+ if( _rg_music_current_check(self) )
668
+ {
669
+ /* Return true if it's not playing. */
670
+ if( !Mix_PlayingMusic() )
671
+ {
672
+ return Qtrue;
673
+ }
674
+ else
675
+ {
676
+ return Qfalse;
677
+ }
678
+ }
679
+ else
680
+ {
681
+ return Qtrue;
682
+ }
683
+ }
684
+
685
+
686
+ /*
687
+ * call-seq:
688
+ * fade_out( fade_time ) -> self
689
+ *
690
+ * Fade out to silence over the given number of seconds. Once the music
691
+ * is silent, it is automatically stopped.
692
+ *
693
+ * Returns:: The receiver (self).
694
+ *
695
+ * **NOTE**: If the music is currently paused, the fade will start,
696
+ * but you won't be able to hear it happening unless you #unpause during
697
+ * the fade.
698
+ *
699
+ * Does nothing if the music is currently stopped.
700
+ *
701
+ */
702
+ static VALUE rg_music_fadeout( VALUE self, VALUE fade_time )
703
+ {
704
+ RG_Music *music;
705
+ Data_Get_Struct(self, RG_Music, music);
706
+
707
+ int fade_ms = (int)(1000 * NUM2DBL(fade_time));
708
+
709
+ if( fade_ms < 0 )
710
+ {
711
+ rb_raise(rb_eArgError, "fade_time cannot be negative (got %.2f)",
712
+ fade_ms / 1000);
713
+ }
714
+
715
+ /* Check that the music is current */
716
+ if( _rg_music_current_check(self) )
717
+ {
718
+ int result = Mix_FadeOutMusic( fade_ms );
719
+
720
+ if ( result < 0 )
721
+ {
722
+ rb_raise(eSDLError, "Error fading out music: %s", Mix_GetError());
723
+ }
724
+
725
+ }
726
+
727
+ return self;
728
+ }
729
+
730
+
731
+ /*
732
+ * call-seq:
733
+ * fading?( direction=:either ) -> true or false
734
+ *
735
+ * True if the Music is currently fading in or out.
736
+ * See also #play and #fade_out.
737
+ *
738
+ * direction:: Check if it is fading :in, :out, or :either.
739
+ * (Symbol, required)
740
+ *
741
+ */
742
+ static VALUE rg_music_fadingp( int argc, VALUE *argv, VALUE self )
743
+ {
744
+ RG_Music *music;
745
+ Data_Get_Struct(self, RG_Music, music);
746
+
747
+ VALUE vdirection;
748
+ rb_scan_args(argc, argv, "01", &vdirection);
749
+
750
+ /* If the music is not current, return false right away. */
751
+ if( !(_rg_music_current_check(self)) )
752
+ {
753
+ return Qfalse;
754
+ }
755
+
756
+ if( RTEST(vdirection) )
757
+ {
758
+ if( make_symbol("in") == vdirection )
759
+ {
760
+ return ( (Mix_FadingMusic() == MIX_FADING_IN) ? Qtrue : Qfalse );
761
+ }
762
+
763
+ else if( make_symbol("out") == vdirection )
764
+ {
765
+ return ( (Mix_FadingMusic() == MIX_FADING_OUT) ? Qtrue : Qfalse );
766
+ }
767
+
768
+ else if( make_symbol("either") == vdirection )
769
+ {
770
+ return ( (Mix_FadingMusic() != MIX_NO_FADING) ? Qtrue : Qfalse );
771
+ }
772
+ }
773
+
774
+ /* default */
775
+ return ( (Mix_FadingMusic() != MIX_NO_FADING) ? Qtrue : Qfalse );
776
+ }
777
+
778
+
779
+
780
+ /*
781
+ * call-seq:
782
+ * volume -> vol
783
+ *
784
+ * Return the volume level of the music.
785
+ * 0.0 is totally silent, 1.0 is full volume.
786
+ *
787
+ * **NOTE**: Ignores fading in or out.
788
+ *
789
+ */
790
+ static VALUE rg_music_getvolume( VALUE self )
791
+ {
792
+ RG_Music *music;
793
+ Data_Get_Struct(self, RG_Music, music);
794
+
795
+ return rb_float_new(music->volume);
796
+ }
797
+
798
+
799
+ /*
800
+ * call-seq:
801
+ * volume = new_vol
802
+ *
803
+ * Set the new #volume level of the music.
804
+ * 0.0 is totally silent, 1.0 is full volume.
805
+ *
806
+ * Volume cannot be set while the music is fading in or out.
807
+ * Be sure to check #fading? or rescue from SDLError when
808
+ * using this method.
809
+ *
810
+ * May raise:: SDLError if the music is fading in or out.
811
+ *
812
+ */
813
+ static VALUE rg_music_setvolume( VALUE self, VALUE volume )
814
+ {
815
+ RG_Music *music;
816
+ Data_Get_Struct(self, RG_Music, music);
817
+
818
+ /* If the music is current, we'll change the current volume. */
819
+ if( _rg_music_current_check(self) )
820
+ {
821
+ /* But only if it's not fading right now. */
822
+ if( Mix_FadingMusic() == MIX_NO_FADING )
823
+ {
824
+ music->volume = NUM2DBL(volume);
825
+ Mix_VolumeMusic( (int)(MIX_MAX_VOLUME * music->volume) );
826
+ }
827
+ else
828
+ {
829
+ rb_raise(eSDLError, "cannot set Music volume while fading");
830
+ }
831
+ }
832
+ else
833
+ {
834
+ /* Save it for later. */
835
+ music->volume = NUM2DBL(volume);
836
+ }
837
+
838
+ return volume;
839
+ }
840
+
841
+
842
+ /*
843
+ * call-seq:
844
+ * rewind -> self
845
+ *
846
+ * Rewind the Music to the beginning. If the Music was paused, it
847
+ * will still be paused after the rewind. Does nothing if the Music
848
+ * is stopped.
849
+ */
850
+ static VALUE rg_music_rewind( VALUE self )
851
+ {
852
+ RG_Music *music;
853
+ Data_Get_Struct(self, RG_Music, music);
854
+
855
+ /* Check that the music is current. */
856
+ if( _rg_music_current_check(self) )
857
+ {
858
+ /* Only do anything if it's not stopped */
859
+ if( !rg_music_stoppedp(self) )
860
+ {
861
+ /* Remember whether it was paused. */
862
+ int was_paused = Mix_PausedMusic();
863
+
864
+ /*
865
+ * Rather than using SDL_mixer's crippled Mix_RewindMusic,
866
+ * which only works for a few file types, we'll just stop and
867
+ * start the music again from the beginning.
868
+ */
869
+ Mix_HaltMusic();
870
+ int result = Mix_PlayMusic(music->wrap->music, music->repeats);
871
+
872
+ if( result == -1 )
873
+ {
874
+ rb_raise(eSDLError, "Could not rewind Music: %s", Mix_GetError());
875
+ }
876
+
877
+ /* Pause it again if it was paused before. */
878
+ if( was_paused )
879
+ {
880
+ Mix_PauseMusic();
881
+ }
882
+ }
883
+ }
884
+
885
+ return self;
886
+ }
887
+
888
+
889
+ /*
890
+ * call-seq:
891
+ * jump_to( time ) -> self
892
+ *
893
+ * Jump to any time in the Music, in seconds since the beginning.
894
+ * If the Music was paused, it will still be paused again after the
895
+ * jump. Does nothing if the Music was stopped.
896
+ *
897
+ * **NOTE**: Only works for OGG and MP3 formats! Other formats (e.g.
898
+ * WAV) will usually raise SDLError.
899
+ *
900
+ * time:: the time to jump to, in seconds since the beginning
901
+ * of the song. (Numeric, required)
902
+ *
903
+ * May raise:: SDLError if something goes wrong, or if the music
904
+ * type does not support jumping.
905
+ *
906
+ * **CAUTION**: This method may be unreliable (and could even crash!)
907
+ * if you jump to a time after the end of the song. Unfortunately,
908
+ * SDL_Mixer does not provide a way to find the song's length, so
909
+ * Rubygame cannot warn you if you go off the end. Be careful!
910
+ */
911
+ static VALUE rg_music_jumpto( VALUE self, VALUE vtime )
912
+ {
913
+ RG_Music *music;
914
+ Data_Get_Struct(self, RG_Music, music);
915
+
916
+ /* Check that the music is current. */
917
+ if( _rg_music_current_check(self) )
918
+ {
919
+ /* Only do anything if it's not stopped */
920
+ if( !rg_music_stoppedp(self) )
921
+ {
922
+ /* Remember whether it was paused. */
923
+ int was_paused = Mix_PausedMusic();
924
+
925
+ double time = NUM2DBL(vtime); /* in seconds */
926
+
927
+ if( time < 0 )
928
+ {
929
+ rb_raise(rb_eArgError, "jump_to time cannot be negative (got %d)", time);
930
+ }
931
+
932
+ int result = Mix_SetMusicPosition( time );
933
+
934
+ if( result == -1 )
935
+ {
936
+ rb_raise(eSDLError, "Could not jump Music: %s", Mix_GetError());
937
+ }
938
+
939
+ /* Pause it again if it was paused before. */
940
+ if( was_paused )
941
+ {
942
+ Mix_PauseMusic();
943
+ }
944
+ }
945
+ }
946
+
947
+ return self;
948
+ }
949
+
950
+
951
+
952
+ void Rubygame_Init_Music()
953
+ {
954
+ #if 0
955
+ mRubygame = rb_define_module("Rubygame");
956
+ #endif
957
+
958
+ /*
959
+ * **IMPORTANT**: Music is only available if Rubygame was compiled
960
+ * with SDL_mixer support!
961
+ *
962
+ * Music holds a song, streamed from an audio file (see #load for
963
+ * supported formats). There are two important differences between
964
+ * the Music and Sound classes:
965
+ *
966
+ * 1. Only one Music can be playing. If you try to play
967
+ * a second song, the first one will be stopped.
968
+ *
969
+ * 2. Music doesn't load the entire audio file, so it can begin
970
+ * quickly and doesn't use much memory. This is good,
971
+ * because songs are usually much longer than sound effects!
972
+ *
973
+ * Music can #play, #pause/#unpause, #stop, #rewind, #jump_to another
974
+ * time, adjust #volume, and #fade_out (fade in by passing an option
975
+ * to #play).
976
+ *
977
+ * Music includes the Rubygame::NamedResource mixin module, which
978
+ * can perform autoloading of music on demand, among other things.
979
+ *
980
+ */
981
+ cMusic = rb_define_class_under(mRubygame,"Music",rb_cObject);
982
+
983
+ /* Include the Rubygame::NamedResource mixin module. */
984
+ rg_include_named_resource(cMusic);
985
+
986
+ rb_define_singleton_method( cMusic, "current_music", rg_music_current, 0 );
987
+ rb_iv_set( cMusic, "@current_music", Qnil );
988
+
989
+
990
+ rb_define_alloc_func( cMusic, rg_music_alloc );
991
+
992
+ rb_define_singleton_method( cMusic, "new", rg_music_new, -1 );
993
+ rb_define_singleton_method( cMusic, "load", rg_music_load, 1 );
994
+
995
+ rb_define_singleton_method( cMusic, "autoload", rg_music_autoload, 1 );
996
+
997
+ rb_define_method( cMusic, "initialize_copy", rg_music_initialize_copy, 1 );
998
+
999
+ rb_define_method( cMusic, "play", rg_music_play, -1 );
1000
+ rb_define_method( cMusic, "playing?", rg_music_playingp, 0 );
1001
+
1002
+ rb_define_method( cMusic, "pause", rg_music_pause, 0 );
1003
+ rb_define_method( cMusic, "unpause", rg_music_unpause, 0 );
1004
+ rb_define_method( cMusic, "paused?", rg_music_pausedp, 0 );
1005
+
1006
+ rb_define_method( cMusic, "stop", rg_music_stop, 0 );
1007
+ rb_define_method( cMusic, "stopped?", rg_music_stoppedp, 0 );
1008
+
1009
+ rb_define_method( cMusic, "fade_out", rg_music_fadeout, 1 );
1010
+ rb_define_method( cMusic, "fading?", rg_music_fadingp, -1 );
1011
+
1012
+ rb_define_method( cMusic, "volume", rg_music_getvolume, 0 );
1013
+ rb_define_method( cMusic, "volume=", rg_music_setvolume, 1 );
1014
+
1015
+ rb_define_method( cMusic, "rewind", rg_music_rewind, 0 );
1016
+ rb_define_method( cMusic, "jump_to", rg_music_jumpto, 1 );
1017
+ }