ruby-sdl2 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/mixer.c ADDED
@@ -0,0 +1,1079 @@
1
+ #ifdef HAVE_SDL_MIXER_H
2
+ #include "rubysdl2_internal.h"
3
+ #include <SDL_mixer.h>
4
+
5
+ static VALUE mMixer;
6
+ static VALUE cChunk;
7
+ static VALUE cMusic;
8
+ static VALUE mChannels;
9
+ static VALUE cGroup;
10
+ static VALUE mMusicChannel;
11
+
12
+ static VALUE playing_chunks = Qnil;
13
+ static VALUE playing_music = Qnil;
14
+
15
+ #define MIX_ERROR() do { HANDLE_ERROR(SDL_SetError(Mix_GetError())); } while(0)
16
+ #define HANDLE_MIX_ERROR(code) \
17
+ do { if ((code) < 0) { MIX_ERROR(); } } while (0)
18
+
19
+ typedef struct Chunk {
20
+ Mix_Chunk* chunk;
21
+ } Chunk;
22
+
23
+ typedef struct Music {
24
+ Mix_Music* music;
25
+ } Music;
26
+
27
+ static void Chunk_free(Chunk* c)
28
+ {
29
+ if (rubysdl2_is_active() && c->chunk)
30
+ Mix_FreeChunk(c->chunk);
31
+ free(c);
32
+ }
33
+
34
+ static VALUE Chunk_new(Mix_Chunk* chunk)
35
+ {
36
+ Chunk* c = ALLOC(Chunk);
37
+ c->chunk = chunk;
38
+ return Data_Wrap_Struct(cChunk, 0, Chunk_free, c);
39
+ }
40
+
41
+ DEFINE_WRAPPER(Mix_Chunk, Chunk, chunk, cChunk, "SDL2::Mixer::Chunk");
42
+
43
+ static void Music_free(Music* m)
44
+ {
45
+ if (rubysdl2_is_active() && m->music)
46
+ Mix_FreeMusic(m->music);
47
+ free(m);
48
+ }
49
+
50
+ static VALUE Music_new(Mix_Music* music)
51
+ {
52
+ Music* c = ALLOC(Music);
53
+ c->music = music;
54
+ return Data_Wrap_Struct(cMusic, 0, Music_free, c);
55
+ }
56
+
57
+ DEFINE_WRAPPER(Mix_Music, Music, music, cMusic, "SDL2::Mixer::Music");
58
+
59
+ /*
60
+ * Document-module: SDL2::Mixer
61
+ *
62
+ * Sound mixing module.
63
+ *
64
+ * With this module, you can play many kinds of sound files such as:
65
+ *
66
+ * * WAVE/RIFF (.wav)
67
+ * * AIFF (.aiff)
68
+ * * VOC (.voc)
69
+ * * MOD (.mod .xm .s3m .669 .it .med etc.)
70
+ * * MIDI (.mid)
71
+ * * OggVorbis (.ogg)
72
+ * * MP3 (.mp3)
73
+ * * FLAC (.flac)
74
+ *
75
+ * Before playing sounds,
76
+ * you need to initialize this module by {.init} and
77
+ * open a sound device by {.open}.
78
+ *
79
+ * This module mixes multiple sound sources in parallel.
80
+ * To play a sound source, you assign the source to a "channel"
81
+ * and this module mixes all sound sources assigned to the channels.
82
+ *
83
+ * In this module, there are two types of sound sources:
84
+ * {SDL2::Mixer::Chunk} and {SDL2::Mixer::Music}.
85
+ * And there are two corresponding types of channels:
86
+ * {SDL2::Mixer::Channels} and {SDL2::Mixer::MusicChannel}.
87
+ *
88
+ * {SDL2::Mixer::Channels} module plays {SDL2::Mixer::Chunk} objects,
89
+ * through multiple (default eight) channels. This module is suitable
90
+ * for the sound effects.
91
+ * The number of channels is variable with {SDL2::Mixer::Channels.allocate}.
92
+ *
93
+ * {SDL2::Mixer::MusicChannel} module plays {SDL2::Mixer::Music} objects.
94
+ * This module has only one playing channel, and you cannot play
95
+ * multiple music in parallel. However an {SDL2::Mixer::Music} object
96
+ * is more efficient for memory, and this module supports more file formats
97
+ * than {SDL2::Mixer::Channels}.
98
+ * This module is suitable for playing "BGMs" of your application.
99
+ *
100
+ */
101
+
102
+ /*
103
+ * @overload init(flags)
104
+ * Initialize the mixer library.
105
+ *
106
+ * This module function load dynamically-linked libraries for sound file
107
+ * formats such as ogg and flac.
108
+ *
109
+ * You can give the initialized libraries (file formats) with OR'd bits of the
110
+ * following constants:
111
+ *
112
+ * * SDL2::Mixer::INIT_FLAC
113
+ * * SDL2::Mixer::INIT_MOD
114
+ * * SDL2::Mixer::INIT_MODPLUG
115
+ * * SDL2::Mixer::INIT_MP3
116
+ * * SDL2::Mixer::INIT_OGG
117
+ * * SDL2::Mixer::INIT_FLUIDSYNTH
118
+ *
119
+ * @param flags [Integer] intialized sublibraries
120
+ * @return [nil]
121
+ *
122
+ */
123
+ static VALUE Mixer_s_init(VALUE self, VALUE f)
124
+ {
125
+ int flags = NUM2INT(f);
126
+ if (Mix_Init(flags) & flags != flags)
127
+ rb_raise(eSDL2Error, "Couldn't initialize SDL_mixer");
128
+
129
+ return Qnil;
130
+ }
131
+
132
+ static void check_channel(VALUE ch, int allow_minus_1)
133
+ {
134
+ int channel = NUM2INT(ch);
135
+ if (channel >= Mix_AllocateChannels(-1))
136
+ rb_raise(rb_eArgError, "too large number of channel (%d)", channel);
137
+ if (channel == -1 && !allow_minus_1 || channel < -1)
138
+ rb_raise(rb_eArgError, "negative number of channel is not allowed");
139
+ }
140
+
141
+ /*
142
+ * @overload open(freq=22050, format=SDL2::Mixer::DEFAULT_FORMAT, channels=2, chunksize=1024)
143
+ * Open a sound device.
144
+ *
145
+ * Before calling loading/playing methods in the mixer module,
146
+ * this method must be called.
147
+ * Before calling this method,
148
+ * {SDL2.init} must be called with SDL2::INIT_AUDIO.
149
+ *
150
+ * @param freq [Integer] output sampling frequency in Hz.
151
+ * Normally 22050 or 44100 is used.
152
+ * 44100 is CD audio rate. SDL2::Mixer::DEFAULT_FREQUENCY(22050) is best for
153
+ * many kinds of game because 44100 requires too much CPU power on older computers.
154
+ * @param format [Integer] output sample format
155
+ * @param channels 1 is for mono, and 2 is for stereo.
156
+ * @param chunksize bytes used per output sample
157
+ *
158
+ * @return [nil]
159
+ *
160
+ * @raise [SDL2::Error] raised when a device cannot be opened
161
+ *
162
+ * @see .init
163
+ * @see .close
164
+ * @see .query
165
+ */
166
+ static VALUE Mixer_s_open(int argc, VALUE* argv, VALUE self)
167
+ {
168
+ VALUE freq, format, channels, chunksize;
169
+ rb_scan_args(argc, argv, "04", &freq, &format, &channels, &chunksize);
170
+ HANDLE_MIX_ERROR(Mix_OpenAudio((freq == Qnil) ? MIX_DEFAULT_FREQUENCY : NUM2INT(freq),
171
+ (format == Qnil) ? MIX_DEFAULT_FORMAT : NUM2UINT(format),
172
+ (channels == Qnil) ? 2 : NUM2INT(channels),
173
+ (chunksize == Qnil) ? 1024 : NUM2INT(chunksize)));
174
+ playing_chunks = rb_ary_new();
175
+ return Qnil;
176
+ }
177
+
178
+ /*
179
+ * Close the audio device.
180
+ *
181
+ * @return [nil]
182
+ */
183
+ static VALUE Mixer_s_close(VALUE self)
184
+ {
185
+ Mix_CloseAudio();
186
+ return Qnil;
187
+ }
188
+
189
+
190
+ /*
191
+ * Query a sound device spec.
192
+ *
193
+ * This method returns the most suitable setting for {.open} the device.
194
+ *
195
+ * @return [[Integer, Integer, Integer, Integer]]
196
+ * the suitable frequency in Hz, the suitable format,
197
+ * the suitable number of channels (1 for mono, 2 for stereo),
198
+ * and the number of call of {.open}.
199
+ *
200
+ */
201
+ static VALUE Mixer_s_query(VALUE self)
202
+ {
203
+ int frequency = 0, channels = 0, num_opened;
204
+ Uint16 format = 0;
205
+
206
+ num_opened = Mix_QuerySpec(&frequency, &format, &channels);
207
+ return rb_ary_new3(4, INT2NUM(frequency), UINT2NUM(format),
208
+ INT2NUM(channels), INT2NUM(num_opened));
209
+ }
210
+
211
+ /*
212
+ * Document-module: SDL2::Mixer::Channels
213
+ *
214
+ * This module plays {SDL2::Mixer::Chunk} objects in parallel.
215
+ *
216
+ * Each virtual sound output device is called channel, and
217
+ * the number of channels determines the f
218
+ */
219
+
220
+ /*
221
+ * @overload allocate(num_channels)
222
+ * Set the number of channels being mixed.
223
+ *
224
+ * @param num_channels [Integer] Number of channels prepared for mixing.
225
+ *
226
+ * @return [Integer] the number of prepared channels.
227
+ */
228
+ static VALUE Channels_s_allocate(VALUE self, VALUE num_channels)
229
+ {
230
+ return INT2NUM(Mix_AllocateChannels(NUM2INT(num_channels)));
231
+ }
232
+
233
+ /*
234
+ * @overload reserve(num)
235
+ * @param num [Integer]
236
+ * @return [Integer]
237
+ */
238
+ static VALUE Channels_s_reserve(VALUE self, VALUE num)
239
+ {
240
+ return INT2NUM(Mix_ReserveChannels(NUM2INT(num)));
241
+ }
242
+
243
+ /*
244
+ * @overload volume(channel)
245
+ * Get the volume of specified channel.
246
+ *
247
+ * @param channel [Integer] the channel to get volume for.
248
+ * If the specified channel is -1, this method returns
249
+ * the average volume of all channels.
250
+ * @return [Integer] the volume, 0-128
251
+ *
252
+ * @see .set_volume
253
+ */
254
+ static VALUE Channels_s_volume(VALUE self, VALUE channel)
255
+ {
256
+ return INT2NUM(Mix_Volume(NUM2INT(channel), -1));
257
+ }
258
+
259
+ /*
260
+ * @overload set_volume(channel, volume)
261
+ * Set the volume of specified channel.
262
+ *
263
+ * The volume should be from 0 to {SDL2::Mixer::MAX_VOLUME}(128).
264
+ * If the specified channel is -1, set volume for all channels.
265
+ *
266
+ * @param channel [Integer] the channel to set volume for.
267
+ * @param volume [Integer] the volume to use
268
+ * @return [void]
269
+ *
270
+ * @see .volume
271
+ */
272
+ static VALUE Channels_s_set_volume(VALUE self, VALUE channel, VALUE volume)
273
+ {
274
+ return INT2NUM(Mix_Volume(NUM2INT(channel), NUM2INT(volume)));
275
+ }
276
+
277
+ static void protect_playing_chunk_from_gc(int channel, VALUE chunk)
278
+ {
279
+ rb_ary_store(playing_chunks, channel, chunk);
280
+ }
281
+
282
+ /*
283
+ * @overload play(channel, chunk, loops, ticks = -1)
284
+ * Play a {SDL2::Mixer::Chunk} on **channel**.
285
+ *
286
+ * @param channel [Integer] the channel to play, or -1 for the first free unreserved
287
+ * channel
288
+ * @param chunk [SDL2::Mixer::Chunk] the chunk to play
289
+ * @param loops [Integer] the number of loops, or -1 for infite loops.
290
+ * passing 1 plays the sample twice (1 loop).
291
+ * @param ticks [Integer] milliseconds limit to play, at most.
292
+ * If the chunk is long enough and **loops** is large enough,
293
+ * the play will stop after **ticks** milliseconds.
294
+ * Otherwise, the play will stop when the loop ends.
295
+ * -1 means infinity.
296
+ * @return [Integer] the channel that plays the chunk.
297
+ *
298
+ * @raise [SDL2::Error] raised on a playing error. For example,
299
+ * **channel** is out of the allocated channels, or
300
+ * there is no free channels when **channel** is -1.
301
+ *
302
+ * @see .fade_in
303
+ */
304
+ static VALUE Channels_s_play(int argc, VALUE* argv, VALUE self)
305
+ {
306
+ VALUE channel, chunk, loops, ticks;
307
+ int ch;
308
+ rb_scan_args(argc, argv, "31", &channel, &chunk, &loops, &ticks);
309
+ if (ticks == Qnil)
310
+ ticks = INT2FIX(-1);
311
+ check_channel(channel, 1);
312
+ ch = Mix_PlayChannelTimed(NUM2INT(channel), Get_Mix_Chunk(chunk),
313
+ NUM2INT(loops), NUM2INT(ticks));
314
+ HANDLE_MIX_ERROR(ch);
315
+ protect_playing_chunk_from_gc(ch, chunk);
316
+ return INT2FIX(ch);
317
+ }
318
+
319
+ /*
320
+ * @overload fade_in(channel, chunk, loops, ms, ticks = -1)
321
+ * Play a {SDL2::Mixer::Chunk} on **channel** with fading in.
322
+ *
323
+ * @param channel [Integer] the channel to play, or -1 for the first free unreserved
324
+ * channel
325
+ * @param chunk [SDL2::Mixer::Chunk] the chunk to play
326
+ * @param loops [Integer] the number of loops, or -1 for infite loops.
327
+ * passing 1 plays the sample twice (1 loop).
328
+ * @param ms [Integer] milliseconds of time of fade-in effect.
329
+ * @param ticks [Integer] milliseconds limit to play, at most.
330
+ * If the chunk is long enough and **loops** is large enough,
331
+ * the play will stop after **ticks** milliseconds.
332
+ * Otherwise, the play will stop when the loop ends.
333
+ * -1 means infinity.
334
+ * @return [Integer] the channel that plays the chunk.
335
+ *
336
+ * @raise [SDL2::Error] raised on a playing error. For example,
337
+ * **channel** is out of the allocated channels, or
338
+ * there is no free channels when **channel** is -1.
339
+ *
340
+ * @see .play
341
+ * @see .fade_out
342
+ */
343
+ static VALUE Channels_s_fade_in(int argc, VALUE* argv, VALUE self)
344
+ {
345
+ VALUE channel, chunk, loops, ms, ticks;
346
+ int ch;
347
+ rb_scan_args(argc, argv, "41", &channel, &chunk, &loops, &ms, &ticks);
348
+ if (ticks == Qnil)
349
+ ticks = INT2FIX(-1);
350
+ check_channel(channel, 1);
351
+ ch = Mix_FadeInChannelTimed(NUM2INT(channel), Get_Mix_Chunk(chunk),
352
+ NUM2INT(loops), NUM2INT(ms), NUM2INT(ticks));
353
+ HANDLE_MIX_ERROR(ch);
354
+ protect_playing_chunk_from_gc(ch, chunk);
355
+ return INT2FIX(ch);
356
+ }
357
+
358
+ /*
359
+ * @overload pause(channel)
360
+ * Pause a specified channel.
361
+ *
362
+ * @param channel [Integer] the channel to pause, or -1 for all channels.
363
+ * @return [nil]
364
+ *
365
+ * @see .resume
366
+ * @see .pause?
367
+ */
368
+ static VALUE Channels_s_pause(VALUE self, VALUE channel)
369
+ {
370
+ check_channel(channel, 1);
371
+ Mix_Pause(NUM2INT(channel));
372
+ return Qnil;
373
+ }
374
+
375
+ /*
376
+ * @overload resume(channel)
377
+ * Resume a specified channel that already pauses.
378
+ *
379
+ * @note This method has no effect to unpaused channels.
380
+ * @param channel [Integer] the channel to be resumed, or -1 for all channels.
381
+ * @return [nil]
382
+ *
383
+ * @see .pause
384
+ * @see .pause?
385
+ */
386
+ static VALUE Channels_s_resume(VALUE self, VALUE channel)
387
+ {
388
+ check_channel(channel, 1);
389
+ Mix_Resume(NUM2INT(channel));
390
+ return Qnil;
391
+ }
392
+
393
+ /*
394
+ * @overload halt(channel)
395
+ * Halt playing of a specified channel.
396
+ *
397
+ * @param channel [Integer] the channel to be halted, or -1 for all channels.
398
+ * @return [nil]
399
+ *
400
+ * @see .expire
401
+ * @see .fade_out
402
+ * @see .play?
403
+ */
404
+ static VALUE Channels_s_halt(VALUE self, VALUE channel)
405
+ {
406
+ check_channel(channel, 1);
407
+ Mix_HaltChannel(NUM2INT(channel));
408
+ return Qnil;
409
+ }
410
+
411
+ /*
412
+ * @overload expire(channel, ticks)
413
+ * Halt playing of a specified channel after **ticks** milliseconds.
414
+ *
415
+ * @param channel [Integer] the channel to be halted, or -1 for all channels.
416
+ * @param ticks [Integer] milliseconds untils the channel halts playback.
417
+ * @return [nil]
418
+ *
419
+ * @see .halt
420
+ * @see .fade_out
421
+ * @see .play?
422
+ */
423
+ static VALUE Channels_s_expire(VALUE self, VALUE channel, VALUE ticks)
424
+ {
425
+ check_channel(channel, 1);
426
+ Mix_ExpireChannel(NUM2INT(channel), NUM2INT(ticks));
427
+ return Qnil;
428
+ }
429
+
430
+ /*
431
+ * @overload fade_out(channel, ms)
432
+ * Halt playing of a specified channel with fade-out effect.
433
+ *
434
+ * @param channel [Integer] the channel to be halted, or -1 for all channels.
435
+ * @param ms [Integer] milliseconds of fade-out effect
436
+ * @return [nil]
437
+ *
438
+ * @see .halt
439
+ * @see .expire
440
+ * @see .play?
441
+ * @see .fade_in
442
+ */
443
+ static VALUE Channels_s_fade_out(VALUE self, VALUE channel, VALUE ms)
444
+ {
445
+ check_channel(channel, 1);
446
+ Mix_FadeOutChannel(NUM2INT(channel), NUM2INT(ms));
447
+ return Qnil;
448
+ }
449
+
450
+ /*
451
+ * @overload play?(channel)
452
+ * Return true if a specified channel is playing.
453
+ *
454
+ * @param channel [Integer] channel to test
455
+ * @see .pause?
456
+ * @see .fading
457
+ */
458
+ static VALUE Channels_s_play_p(VALUE self, VALUE channel)
459
+ {
460
+ check_channel(channel, 0);
461
+ return INT2BOOL(Mix_Playing(NUM2INT(channel)));
462
+ }
463
+
464
+ /*
465
+ * @overload pause?(channel)
466
+ * Return true if a specified channel is paused.
467
+ *
468
+ * @note This method returns true if a paused channel is halted by {.halt}, or any
469
+ * other halting methods.
470
+ *
471
+ * @param channel [Integer] channel to test
472
+ *
473
+ * @see .play?
474
+ * @see .fading
475
+ */
476
+ static VALUE Channels_s_pause_p(VALUE self, VALUE channel)
477
+ {
478
+ check_channel(channel, 0);
479
+ return INT2BOOL(Mix_Paused(NUM2INT(channel)));
480
+ }
481
+
482
+ /*
483
+ * @overload fading(channel)
484
+ * Return the fading state of a specified channel.
485
+ *
486
+ * The return value is one of the following:
487
+ *
488
+ * * {SDL2::Mixer::NO_FADING} - **channel** is not fading in, and fading out
489
+ * * {SDL2::Mixer::FADING_IN} - **channel** is fading in
490
+ * * {SDL2::Mixer::FADING_OUT} - **channel** is fading out
491
+ *
492
+ * @param channel [Integer] channel to test
493
+ *
494
+ * @return [Integer]
495
+ *
496
+ * @see .play?
497
+ * @see .pause?
498
+ * @see .fade_in
499
+ * @see .fade_out
500
+ */
501
+ static VALUE Channels_s_fading(VALUE self, VALUE which)
502
+ {
503
+ check_channel(which, 0);
504
+ return INT2FIX(Mix_FadingChannel(NUM2INT(which)));
505
+ }
506
+
507
+ /*
508
+ * @overload playing_chunk(channel)
509
+ * Get the {SDL2::Mixer::Chunk} object most recently playing on **channel**.
510
+ *
511
+ * If **channel** is out of allocated channels, or
512
+ * no chunk is played yet on **channel**, this method returns nil.
513
+ *
514
+ * @param channel [Integer] the channel to get the chunk object
515
+ * @return [SDL2::Mixer::Chunk,nil]
516
+ */
517
+ static VALUE Channels_s_playing_chunk(VALUE self, VALUE channel)
518
+ {
519
+ check_channel(channel, 0);
520
+ return rb_ary_entry(playing_chunks, NUM2INT(channel));
521
+ }
522
+
523
+ static VALUE Group_initialize(VALUE self, VALUE tag)
524
+ {
525
+ rb_iv_set(self, "@tag", tag);
526
+ return Qnil;
527
+ }
528
+
529
+ static VALUE Group_s_default(VALUE self)
530
+ {
531
+ VALUE tag = INT2FIX(-1);
532
+ return rb_class_new_instance(1, &tag, self);
533
+ }
534
+
535
+ inline static int Group_tag(VALUE group)
536
+ {
537
+ return NUM2INT(rb_iv_get(group, "@tag"));
538
+ }
539
+
540
+ static VALUE Group_eq(VALUE self, VALUE other)
541
+ {
542
+ return INT2BOOL(rb_obj_is_instance_of(other, cGroup) &&
543
+ Group_tag(self) == Group_tag(other));
544
+ }
545
+
546
+ static VALUE Group_add(VALUE self, VALUE which)
547
+ {
548
+ if (!Mix_GroupChannel(NUM2INT(which), Group_tag(self))) {
549
+ SDL_SetError("Cannot add channel %d", NUM2INT(which));
550
+ SDL_ERROR();
551
+ }
552
+ return Qnil;
553
+ }
554
+
555
+ static VALUE Group_count(VALUE self)
556
+ {
557
+ return INT2NUM(Mix_GroupCount(Group_tag(self)));
558
+ }
559
+
560
+ static VALUE Group_available(VALUE self)
561
+ {
562
+ return INT2NUM(Mix_GroupAvailable(Group_tag(self)));
563
+ }
564
+
565
+ static VALUE Group_oldest(VALUE self)
566
+ {
567
+ return INT2NUM(Mix_GroupOldest(Group_tag(self)));
568
+ }
569
+
570
+ static VALUE Group_newer(VALUE self)
571
+ {
572
+ return INT2NUM(Mix_GroupNewer(Group_tag(self)));
573
+ }
574
+
575
+ static VALUE Group_fade_out(VALUE self, VALUE ms)
576
+ {
577
+ return INT2NUM(Mix_FadeOutGroup(Group_tag(self), NUM2INT(ms)));
578
+ }
579
+
580
+ static VALUE Group_halt(VALUE self)
581
+ {
582
+ Mix_HaltGroup(Group_tag(self));
583
+ return Qnil;
584
+ }
585
+
586
+ /*
587
+ * @overload play(music, loops)
588
+ * Play **music** **loops** times.
589
+ *
590
+ * @note the meaning of **loop** is different from {SDL2::Mixer::Channels.play}.
591
+ *
592
+ * @param music [SDL2::Mixer::Music] music to play
593
+ * @param loops [Integer] number of times to play the music.
594
+ * 0 plays the music zero times.
595
+ * -1 plays the music forever.
596
+ *
597
+ * @return [nil]
598
+ *
599
+ * @see {.fade_in}
600
+ *
601
+ */
602
+ static VALUE MusicChannel_s_play(VALUE self, VALUE music, VALUE loops)
603
+ {
604
+ HANDLE_MIX_ERROR(Mix_PlayMusic(Get_Mix_Music(music), NUM2INT(loops)));
605
+ playing_music = music;
606
+ return Qnil;
607
+ }
608
+
609
+ /*
610
+ * @overload fade_in(music, loops, ms, pos=0)
611
+ * Play **music** **loops** times with fade-in effect.
612
+ *
613
+ * @note the meaning of **loop** is different from {SDL2::Mixer::Channels.play}.
614
+ *
615
+ * @param music [SDL2::Mixer::Music] music to play
616
+ * @param loops [Integer] number of times to play the music.
617
+ * 0 plays the music zero times.
618
+ * -1 plays the music forever.
619
+ * @param ms [Integer] milliseconds for the fade-in effect
620
+ * @param pos [Float] the position to play from.
621
+ * The meaning of "position" is different for the type of music sources.
622
+ *
623
+ * @return [nil]
624
+ *
625
+ * @see {.play}
626
+ */
627
+ static VALUE MusicChannel_s_fade_in(int argc, VALUE* argv, VALUE self)
628
+ {
629
+ VALUE music, loops, fade_in_ms, pos;
630
+ rb_scan_args(argc, argv, "31", &music, &loops, &fade_in_ms, &pos);
631
+ HANDLE_MIX_ERROR(Mix_FadeInMusicPos(Get_Mix_Music(music), NUM2INT(loops),
632
+ NUM2INT(fade_in_ms),
633
+ pos == Qnil ? 0 : NUM2DBL(pos)));
634
+ playing_music = music;
635
+ return Qnil;
636
+ }
637
+
638
+ /*
639
+ * Get the volume of the music channel.
640
+ *
641
+ * @return [Integer]
642
+ *
643
+ * @see .volume=
644
+ */
645
+ static VALUE MusicChannel_s_volume(VALUE self)
646
+ {
647
+ return INT2FIX(Mix_VolumeMusic(-1));
648
+ }
649
+
650
+ /*
651
+ * @overload volume=(vol)
652
+ * Set the volume of the music channel.
653
+ *
654
+ * @param vol [Integer] the volume for mixing,
655
+ * from 0 to {SDL2::Mixer::MAX_VOLUME}(128).
656
+ * @return [vol]
657
+ *
658
+ * @see .volume
659
+ */
660
+ static VALUE MusicChannel_s_set_volume(VALUE self, VALUE volume)
661
+ {
662
+ Mix_VolumeMusic(NUM2INT(volume));
663
+ return volume;
664
+ }
665
+
666
+ /*
667
+ * Pause the playback of the music channel.
668
+ *
669
+ * @return [nil]
670
+ *
671
+ * @see .resume
672
+ * @see .pause?
673
+ */
674
+ static VALUE MusicChannel_s_pause(VALUE self)
675
+ {
676
+ Mix_PauseMusic(); return Qnil;
677
+ }
678
+
679
+ /*
680
+ * Resume the playback of the music channel.
681
+ *
682
+ * @return [nil]
683
+ *
684
+ * @see .pause
685
+ * @see .pause?
686
+ */
687
+ static VALUE MusicChannel_s_resume(VALUE self)
688
+ {
689
+ Mix_ResumeMusic(); return Qnil;
690
+ }
691
+
692
+ /*
693
+ * Rewind the music to the start.
694
+ *
695
+ * @return [nil]
696
+ */
697
+ static VALUE MusicChannel_s_rewind(VALUE self)
698
+ {
699
+ Mix_RewindMusic(); return Qnil;
700
+ }
701
+
702
+ /*
703
+ * @overload set_position(position)
704
+ * Set the position of the currently playing music.
705
+ *
706
+ * @param position [Float] the position to play from.
707
+ * @return [nil]
708
+ */
709
+ static VALUE MusicChannel_s_set_position(VALUE self, VALUE position)
710
+ {
711
+ HANDLE_MIX_ERROR(Mix_SetMusicPosition(NUM2DBL(position)));
712
+ return Qnil;
713
+ }
714
+
715
+ /*
716
+ * Halt the music playback.
717
+ *
718
+ * @return [nil]
719
+ */
720
+ static VALUE MusicChannel_s_halt(VALUE self)
721
+ {
722
+ Mix_HaltMusic(); return Qnil;
723
+ }
724
+
725
+ /*
726
+ * @overload fade_out(ms)
727
+ * Halt the music playback with fade-out effect.
728
+ *
729
+ * @return [nil]
730
+ */
731
+ static VALUE MusicChannel_s_fade_out(VALUE self, VALUE fade_out_ms)
732
+ {
733
+ Mix_FadeOutMusic(NUM2INT(fade_out_ms)); return Qnil;
734
+ }
735
+
736
+ /*
737
+ * Return true if a music is playing.
738
+ */
739
+ static VALUE MusicChannel_s_play_p(VALUE self)
740
+ {
741
+ return INT2BOOL(Mix_PlayingMusic());
742
+ }
743
+
744
+ /*
745
+ * Return true if a music playback is paused.
746
+ */
747
+ static VALUE MusicChannel_s_pause_p(VALUE self)
748
+ {
749
+ return INT2BOOL(Mix_PausedMusic());
750
+ }
751
+
752
+ /*
753
+ * Get the fading state of the music playback.
754
+ *
755
+ * The return value is one of the following:
756
+ *
757
+ * * {SDL2::Mixer::NO_FADING} - not fading in, and fading out
758
+ * * {SDL2::Mixer::FADING_IN} - fading in
759
+ * * {SDL2::Mixer::FADING_OUT} - fading out
760
+ *
761
+ * @return [Integer]
762
+ *
763
+ * @see .fade_in
764
+ * @see .fade_out
765
+ *
766
+ */
767
+ static VALUE MusicChannel_s_fading(VALUE self)
768
+ {
769
+ return INT2NUM(Mix_FadingMusic());
770
+ }
771
+
772
+ /*
773
+ * Get the {SDL2::Mixer::Music} object that most recently played.
774
+ *
775
+ * Return nil if no music object is played yet.
776
+ *
777
+ * @return [SDL2::Mixer::Music,nil]
778
+ */
779
+ static VALUE MusicChannel_s_playing_music(VALUE self)
780
+ {
781
+ return playing_music;
782
+ }
783
+
784
+ /*
785
+ * Document-class: SDL2::Mixer::Chunk
786
+ *
787
+ * This class represents a sound sample, a kind of sound sources.
788
+ *
789
+ * Chunk objects is playable on {SDL2::Mixer::Channels}.
790
+ *
791
+ * @!method destroy?
792
+ * Return true if the memory is deallocated by {#destroy}.
793
+ */
794
+
795
+ /*
796
+ * @overload load(path)
797
+ * Load a sample from file.
798
+ *
799
+ * This can load WAVE, AIFF, RIFF, OGG, and VOC files.
800
+ *
801
+ * @note {SDL2::Mixer.open} must be called before calling this method.
802
+ *
803
+ * @param path [String] the fine name
804
+ * @return [SDL2::Mixer::Chunk]
805
+ *
806
+ * @raise [SDL2::Error] raised when failing to load
807
+ */
808
+ static VALUE Chunk_s_load(VALUE self, VALUE fname)
809
+ {
810
+ Mix_Chunk* chunk = Mix_LoadWAV(StringValueCStr(fname));
811
+ VALUE c;
812
+ if (!chunk)
813
+ MIX_ERROR();
814
+ c = Chunk_new(chunk);
815
+ rb_iv_set(c, "@filename", fname);
816
+ return c;
817
+ }
818
+
819
+ /*
820
+ * Get the names of the sample decoders.
821
+ *
822
+ * @return [Array<String>] the names of decoders, such as: "WAVE", "OGG", etc.
823
+ */
824
+ static VALUE Chunk_s_decoders(VALUE self)
825
+ {
826
+ int i;
827
+ int num_decoders = Mix_GetNumChunkDecoders();
828
+ VALUE ary = rb_ary_new();
829
+ for (i=0; i < num_decoders; ++i)
830
+ rb_ary_push(ary, rb_usascii_str_new_cstr(Mix_GetChunkDecoder(i)));
831
+ return ary;
832
+ }
833
+
834
+ /*
835
+ * Deallocate the sample memory.
836
+ *
837
+ * Normally, the memory is deallocated by ruby's GC, but
838
+ * you can surely deallocate the memory with this method at any time.
839
+ *
840
+ * @return [nil]
841
+ */
842
+ static VALUE Chunk_destroy(VALUE self)
843
+ {
844
+ Chunk* c = Get_Chunk(self);
845
+ if (c->chunk) Mix_FreeChunk(c->chunk);
846
+ c->chunk = NULL;
847
+ return Qnil;
848
+ }
849
+
850
+ /*
851
+ * Get the volume of the sample.
852
+ *
853
+ * @return [Integer] the volume from 0 to {SDL2::Mixer::MAX_VOLUME}.
854
+ *
855
+ * @see #volume=
856
+ */
857
+ static VALUE Chunk_volume(VALUE self)
858
+ {
859
+ return INT2NUM(Mix_VolumeChunk(Get_Mix_Chunk(self), -1));
860
+ }
861
+
862
+ /*
863
+ * @overload volume=(vol)
864
+ * Set the volume of the sample.
865
+ *
866
+ * @param vol [Integer] the new volume
867
+ * @return [vol]
868
+ *
869
+ * @see #volume
870
+ */
871
+ static VALUE Chunk_set_volume(VALUE self, VALUE vol)
872
+ {
873
+ return INT2NUM(Mix_VolumeChunk(Get_Mix_Chunk(self), NUM2INT(vol)));
874
+ }
875
+
876
+ /* @return [String] inspection string */
877
+ static VALUE Chunk_inspect(VALUE self)
878
+ {
879
+ VALUE filename = rb_iv_get(self, "@filename");
880
+ if (RTEST(Chunk_destroy_p(self)))
881
+ return rb_sprintf("<%s: destroyed>", rb_obj_classname(self));
882
+
883
+ return rb_sprintf("<%s: filename=\"%s\" volume=%d>",
884
+ rb_obj_classname(self),
885
+ StringValueCStr(filename),
886
+ Mix_VolumeChunk(Get_Mix_Chunk(self), -1));
887
+ }
888
+
889
+ /*
890
+ * Document-class: SDL2::Mixer::Music
891
+ *
892
+ * This class represents music, a kind of sound sources.
893
+ *
894
+ * Music is playable on {SDL2::Mixer::MusicChannel}, not on {SDL2::Mixer::Channels}.
895
+ *
896
+ * @!method destroy?
897
+ * Return true if the memory is deallocated by {#destroy}.
898
+ */
899
+
900
+ /*
901
+ * Get the names of music decoders.
902
+ *
903
+ * @return [Array<String>] the names of decorders (supported sound formats),
904
+ * such as: "OGG", "WAVE", "MP3"
905
+ */
906
+ static VALUE Music_s_decoders(VALUE self)
907
+ {
908
+ int num_decoders = Mix_GetNumMusicDecoders();
909
+ int i;
910
+ VALUE decoders = rb_ary_new2(num_decoders);
911
+ for (i=0; i<num_decoders; ++i)
912
+ rb_ary_push(decoders, utf8str_new_cstr(Mix_GetMusicDecoder(i)));
913
+ return decoders;
914
+ }
915
+
916
+ /*
917
+ * @overload load(path)
918
+ * Load a music from file.
919
+ *
920
+ * @param path [String] the file path
921
+ * @return [SDL2::Mixer::Music]
922
+ *
923
+ * @raise [SDL2::Error] raised when failing to load.
924
+ */
925
+ static VALUE Music_s_load(VALUE self, VALUE fname)
926
+ {
927
+ Mix_Music* music = Mix_LoadMUS(StringValueCStr(fname));
928
+ VALUE mus;
929
+ if (!music) MIX_ERROR();
930
+ mus = Music_new(music);
931
+ rb_iv_set(mus, "@filename", fname);
932
+ return mus;
933
+ }
934
+
935
+ /*
936
+ * Deallocate the music memory.
937
+ *
938
+ * Normally, the memory is deallocated by ruby's GC, but
939
+ * you can surely deallocate the memory with this method at any time.
940
+ *
941
+ * @return [nil]
942
+ */
943
+ static VALUE Music_destroy(VALUE self)
944
+ {
945
+ Music* c = Get_Music(self);
946
+ if (c) Mix_FreeMusic(c->music);
947
+ c->music = NULL;
948
+ return Qnil;
949
+ }
950
+
951
+ /* @return [String] inspection string */
952
+ static VALUE Music_inspect(VALUE self)
953
+ {
954
+ VALUE filename = rb_iv_get(self, "@filename");
955
+ if (RTEST(Music_destroy_p(self)))
956
+ return rb_sprintf("<%s: destroyed>", rb_obj_classname(self));
957
+
958
+ return rb_sprintf("<%s: filename=\"%s\" type=%d>",
959
+ rb_obj_classname(self), StringValueCStr(filename),
960
+ Mix_GetMusicType(Get_Mix_Music(self)));
961
+ }
962
+
963
+
964
+ void rubysdl2_init_mixer(void)
965
+ {
966
+ mMixer = rb_define_module_under(mSDL2, "Mixer");
967
+
968
+ rb_define_module_function(mMixer, "init", Mixer_s_init, 1);
969
+ rb_define_module_function(mMixer, "open", Mixer_s_open, -1);
970
+ rb_define_module_function(mMixer, "close", Mixer_s_close, 0);
971
+ rb_define_module_function(mMixer, "query", Mixer_s_query, 0);
972
+
973
+ #define DEFINE_MIX_INIT(t) \
974
+ rb_define_const(mMixer, "INIT_" #t, UINT2NUM(MIX_INIT_##t))
975
+ DEFINE_MIX_INIT(FLAC);
976
+ DEFINE_MIX_INIT(MOD);
977
+ DEFINE_MIX_INIT(MODPLUG);
978
+ DEFINE_MIX_INIT(MP3);
979
+ DEFINE_MIX_INIT(OGG);
980
+ DEFINE_MIX_INIT(FLUIDSYNTH);
981
+
982
+ #define DEFINE_MIX_FORMAT(t) \
983
+ rb_define_const(mMixer, "FORMAT_" #t, UINT2NUM(AUDIO_##t))
984
+ DEFINE_MIX_FORMAT(U8);
985
+ DEFINE_MIX_FORMAT(S8);
986
+ DEFINE_MIX_FORMAT(U16LSB);
987
+ DEFINE_MIX_FORMAT(S16LSB);
988
+ DEFINE_MIX_FORMAT(U16MSB);
989
+ DEFINE_MIX_FORMAT(S16MSB);
990
+ DEFINE_MIX_FORMAT(U16SYS);
991
+ DEFINE_MIX_FORMAT(S16SYS);
992
+ rb_define_const(mMixer, "DEFAULT_FREQUENCY", UINT2NUM(MIX_DEFAULT_FREQUENCY));
993
+ rb_define_const(mMixer, "DEFAULT_FORMAT", UINT2NUM(MIX_DEFAULT_FORMAT));
994
+ rb_define_const(mMixer, "DEFAULT_CHANNELS", INT2FIX(MIX_DEFAULT_CHANNELS));
995
+ rb_define_const(mMixer, "MAX_VOLUME", INT2FIX(MIX_MAX_VOLUME));
996
+ rb_define_const(mMixer, "NO_FADING", INT2FIX(MIX_NO_FADING));
997
+ rb_define_const(mMixer, "FADING_OUT", INT2FIX(MIX_FADING_OUT));
998
+ rb_define_const(mMixer, "FADING_IN", INT2FIX(MIX_FADING_IN));
999
+
1000
+
1001
+ cChunk = rb_define_class_under(mMixer, "Chunk", rb_cObject);
1002
+ rb_undef_alloc_func(cChunk);
1003
+ rb_define_singleton_method(cChunk, "load", Chunk_s_load, 1);
1004
+ rb_define_singleton_method(cChunk, "decoders", Chunk_s_decoders, 0);
1005
+ rb_define_method(cChunk, "destroy", Chunk_destroy, 0);
1006
+ rb_define_method(cChunk, "destroy?", Chunk_destroy_p, 0);
1007
+ rb_define_method(cChunk, "volume", Chunk_volume, 0);
1008
+ rb_define_method(cChunk, "volume=", Chunk_set_volume, 1);
1009
+ rb_define_method(cChunk, "inspect", Chunk_inspect, 0);
1010
+ rb_define_attr(cChunk, "filename", 1, 0);
1011
+
1012
+
1013
+ cMusic = rb_define_class_under(mMixer, "Music", rb_cObject);
1014
+ rb_undef_alloc_func(cMusic);
1015
+ rb_define_singleton_method(cMusic, "decoders", Music_s_decoders, 0);
1016
+ rb_define_singleton_method(cMusic, "load", Music_s_load, 1);
1017
+ rb_define_method(cMusic, "destroy", Music_destroy, 0);
1018
+ rb_define_method(cMusic, "destroy?", Music_destroy_p, 0);
1019
+ rb_define_method(cMusic, "inspect", Music_inspect, 0);
1020
+
1021
+
1022
+ mChannels = rb_define_module_under(mMixer, "Channels");
1023
+ rb_define_module_function(mChannels, "allocate", Channels_s_allocate, 1);
1024
+ rb_define_module_function(mChannels, "reserve", Channels_s_reserve, 1);
1025
+ rb_define_module_function(mChannels, "volume", Channels_s_volume, 1);
1026
+ rb_define_module_function(mChannels, "set_volume", Channels_s_set_volume, 2);
1027
+ rb_define_module_function(mChannels, "play", Channels_s_play, -1);
1028
+ rb_define_module_function(mChannels, "fade_in", Channels_s_fade_in, -1);
1029
+ rb_define_module_function(mChannels, "pause", Channels_s_pause, 1);
1030
+ rb_define_module_function(mChannels, "resume", Channels_s_resume, 1);
1031
+ rb_define_module_function(mChannels, "halt", Channels_s_halt, 1);
1032
+ rb_define_module_function(mChannels, "expire", Channels_s_expire, 1);
1033
+ rb_define_module_function(mChannels, "fade_out", Channels_s_fade_out, 2);
1034
+ rb_define_module_function(mChannels, "play?", Channels_s_play_p, 1);
1035
+ rb_define_module_function(mChannels, "pause?", Channels_s_pause_p, 1);
1036
+ rb_define_module_function(mChannels, "fading", Channels_s_fading, 1);
1037
+ rb_define_module_function(mChannels, "playing_chunk", Channels_s_playing_chunk, 1);
1038
+
1039
+
1040
+ cGroup = rb_define_class_under(mChannels, "Group", rb_cObject);
1041
+ rb_define_method(cGroup, "initialize", Group_initialize, 1);
1042
+ rb_define_singleton_method(cGroup, "default", Group_s_default, 0);
1043
+ rb_define_attr(cGroup, "tag", 1, 0);
1044
+ rb_define_method(cGroup, "==", Group_eq, 1);
1045
+ rb_define_method(cGroup, "add", Group_add, 1);
1046
+ rb_define_method(cGroup, "count", Group_count, 0);
1047
+ rb_define_method(cGroup, "available", Group_available, 0);
1048
+ rb_define_method(cGroup, "newer", Group_newer, 0);
1049
+ rb_define_method(cGroup, "oldest", Group_oldest, 0);
1050
+ rb_define_method(cGroup, "fade_out", Group_fade_out, 1);
1051
+ rb_define_method(cGroup, "halt", Group_halt, 0);
1052
+
1053
+
1054
+ mMusicChannel = rb_define_module_under(mMixer, "MusicChannel");
1055
+ rb_define_module_function(mMusicChannel, "play", MusicChannel_s_play, 2);
1056
+ rb_define_module_function(mMusicChannel, "fade_in", MusicChannel_s_fade_in, -1);
1057
+ rb_define_module_function(mMusicChannel, "volume", MusicChannel_s_volume, 0);
1058
+ rb_define_module_function(mMusicChannel, "volume=", MusicChannel_s_set_volume, 1);
1059
+ rb_define_module_function(mMusicChannel, "pause", MusicChannel_s_pause, 0);
1060
+ rb_define_module_function(mMusicChannel, "resume", MusicChannel_s_resume, 0);
1061
+ rb_define_module_function(mMusicChannel, "rewind", MusicChannel_s_rewind, 0);
1062
+ rb_define_module_function(mMusicChannel, "set_position", MusicChannel_s_set_position, 1);
1063
+ rb_define_module_function(mMusicChannel, "halt", MusicChannel_s_halt, 0);
1064
+ rb_define_module_function(mMusicChannel, "fade_out", MusicChannel_s_fade_out, 1);
1065
+ rb_define_module_function(mMusicChannel, "play?", MusicChannel_s_play_p, 0);
1066
+ rb_define_module_function(mMusicChannel, "pause?", MusicChannel_s_pause_p, 0);
1067
+ rb_define_module_function(mMusicChannel, "fading", MusicChannel_s_fading, 0);
1068
+ rb_define_module_function(mMusicChannel, "playing_music", MusicChannel_s_playing_music, 0);
1069
+
1070
+
1071
+ rb_gc_register_address(&playing_chunks);
1072
+ rb_gc_register_address(&playing_music);
1073
+ }
1074
+
1075
+ #else /* HAVE_SDL_MIXER_H */
1076
+ void rubysdl2_init_mixer(void)
1077
+ {
1078
+ }
1079
+ #endif