rubygame 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }