ruby-sdl2 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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