/
music.c
1201 lines (1050 loc) · 31.9 KB
1
/*
2
SDL_mixer: An audio mixer library based on the SDL library
3
Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
20
*/
21
#include "SDL_hints.h"
22
#include "SDL_log.h"
23
#include "SDL_timer.h"
24
25
#include "SDL_mixer.h"
26
#include "mixer.h"
27
#include "music.h"
28
29
#include "music_cmd.h"
30
31
#include "music_wav.h"
#include "music_mikmod.h"
32
#include "music_modplug.h"
33
34
35
#include "music_nativemidi.h"
#include "music_fluidsynth.h"
#include "music_timidity.h"
36
#include "music_ogg.h"
37
#include "music_opus.h"
38
#include "music_mpg123.h"
39
#include "music_mad.h"
40
#include "music_flac.h"
41
#include "native_midi/native_midi.h"
42
43
44
#include "compat.h"
45
46
47
48
49
/* Check to make sure we are building with a new enough SDL */
#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 7)
#error You need SDL 2.0.7 or newer from http://www.libsdl.org
#endif
50
51
52
/* Set this hint to true if you want verbose logging of music interfaces */
#define SDL_MIXER_HINT_DEBUG_MUSIC_INTERFACES \
"SDL_MIXER_DEBUG_MUSIC_INTERFACES"
54
char *music_cmd = NULL;
55
56
static SDL_bool music_active = SDL_TRUE;
static int music_volume = MIX_MAX_VOLUME;
57
static Mix_Music * volatile music_playing = NULL;
58
SDL_AudioSpec music_spec;
59
60
struct _Mix_Music {
61
62
63
64
Mix_MusicInterface *interface;
void *context;
SDL_bool playing;
65
66
67
Mix_Fading fading;
int fade_step;
int fade_steps;
68
69
};
70
71
72
/* Used to calculate fading steps */
static int ms_per_step;
73
74
75
76
/* rcg06042009 report available decoders at runtime. */
static const char **music_decoders = NULL;
static int num_decoders = 0;
77
/* Semicolon-separated SoundFont paths */
78
static char* soundfont_paths = NULL;
79
80
81
82
/* full path of timidity config file */
static char* timidity_cfg = NULL;
83
84
85
86
87
88
89
90
91
/* Interfaces for the various music interfaces, ordered by priority */
static Mix_MusicInterface *s_music_interfaces[] =
{
#ifdef MUSIC_CMD
&Mix_MusicInterface_CMD,
#endif
#ifdef MUSIC_WAV
&Mix_MusicInterface_WAV,
#endif
92
93
94
95
96
97
#ifdef MUSIC_FLAC
&Mix_MusicInterface_FLAC,
#endif
#ifdef MUSIC_OGG
&Mix_MusicInterface_OGG,
#endif
98
99
100
#ifdef MUSIC_OPUS
&Mix_MusicInterface_Opus,
#endif
101
102
103
104
105
106
#ifdef MUSIC_MP3_MPG123
&Mix_MusicInterface_MPG123,
#endif
#ifdef MUSIC_MP3_MAD
&Mix_MusicInterface_MAD,
#endif
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#ifdef MUSIC_MOD_MODPLUG
&Mix_MusicInterface_MODPLUG,
#endif
#ifdef MUSIC_MOD_MIKMOD
&Mix_MusicInterface_MIKMOD,
#endif
#ifdef MUSIC_MID_FLUIDSYNTH
&Mix_MusicInterface_FLUIDSYNTH,
#endif
#ifdef MUSIC_MID_TIMIDITY
&Mix_MusicInterface_TIMIDITY,
#endif
#ifdef MUSIC_MID_NATIVE
&Mix_MusicInterface_NATIVEMIDI,
#endif
};
int get_num_music_interfaces(void)
{
return SDL_arraysize(s_music_interfaces);
}
Mix_MusicInterface *get_music_interface(int index)
{
return s_music_interfaces[index];
}
134
int Mix_GetNumMusicDecoders(void)
135
{
136
return(num_decoders);
137
138
139
140
}
const char *Mix_GetMusicDecoder(int index)
{
141
142
143
144
if ((index < 0) || (index >= num_decoders)) {
return NULL;
}
return(music_decoders[index]);
145
146
147
148
}
static void add_music_decoder(const char *decoder)
{
149
150
151
152
153
154
155
156
157
158
void *ptr;
int i;
/* Check to see if we already have this decoder */
for (i = 0; i < num_decoders; ++i) {
if (SDL_strcmp(music_decoders[i], decoder) == 0) {
return;
}
}
159
ptr = SDL_realloc((void *)music_decoders, ((size_t)num_decoders + 1) * sizeof (const char *));
160
161
162
163
164
if (ptr == NULL) {
return; /* oh well, go on without it. */
}
music_decoders = (const char **) ptr;
music_decoders[num_decoders++] = decoder;
165
166
}
167
/* Local low-level functions prototypes */
168
static void music_internal_initialize_volume(void);
169
static void music_internal_volume(int volume);
170
static int music_internal_play(Mix_Music *music, int play_count, double position);
171
static int music_internal_position(double position);
172
static SDL_bool music_internal_playing(void);
173
static void music_internal_halt(void);
174
175
176
/* Support for hooking when the music has finished */
177
static void (SDLCALL *music_finished_hook)(void) = NULL;
178
179
void Mix_HookMusicFinished(void (SDLCALL *music_finished)(void))
180
{
181
Mix_LockAudio();
182
music_finished_hook = music_finished;
183
Mix_UnlockAudio();
184
185
}
186
187
188
189
190
/* Convenience function to fill audio and mix at the specified volume
This is called from many music player's GetAudio callback.
*/
int music_pcm_getaudio(void *context, void *data, int bytes, int volume,
int (*GetSome)(void *context, void *data, int bytes, SDL_bool *done))
191
{
192
193
194
195
Uint8 *snd = (Uint8 *)data;
Uint8 *dst;
int len = bytes;
SDL_bool done = SDL_FALSE;
196
197
198
199
if (volume == MIX_MAX_VOLUME) {
dst = snd;
} else {
200
dst = SDL_stack_alloc(Uint8, (size_t)bytes);
201
202
203
204
205
}
while (len > 0 && !done) {
int consumed = GetSome(context, dst, len, &done);
if (consumed < 0) {
break;
206
207
}
208
209
210
211
212
if (volume == MIX_MAX_VOLUME) {
dst += consumed;
} else {
SDL_MixAudioFormat(snd, dst, music_spec.format, (Uint32)consumed, volume);
snd += consumed;
213
}
214
len -= consumed;
215
}
216
217
218
219
if (volume != MIX_MAX_VOLUME) {
SDL_stack_free(dst);
}
return len;
220
221
}
222
/* Mixing function */
223
void SDLCALL music_mixer(void *udata, Uint8 *stream, int len)
224
{
225
226
(void)udata;
227
while (music_playing && music_active && len > 0) {
228
/* Handle fading */
229
230
if (music_playing->fading != MIX_NO_FADING) {
if (music_playing->fade_step++ < music_playing->fade_steps) {
231
232
233
234
int volume;
int fade_step = music_playing->fade_step;
int fade_steps = music_playing->fade_steps;
235
if (music_playing->fading == MIX_FADING_OUT) {
236
237
238
239
240
241
volume = (music_volume * (fade_steps-fade_step)) / fade_steps;
} else { /* Fading in */
volume = (music_volume * fade_step) / fade_steps;
}
music_internal_volume(volume);
} else {
242
if (music_playing->fading == MIX_FADING_OUT) {
243
music_internal_halt();
244
if (music_finished_hook) {
245
246
247
248
249
250
251
252
music_finished_hook();
}
return;
}
music_playing->fading = MIX_NO_FADING;
}
}
253
254
if (music_playing->interface->GetAudio) {
int left = music_playing->interface->GetAudio(music_playing->context, stream, len);
255
256
if (left != 0) {
/* Either an error or finished playing with data left */
257
258
music_playing->playing = SDL_FALSE;
}
259
260
261
262
263
264
265
266
267
if (left > 0) {
stream += (len - left);
len = left;
} else {
len = 0;
}
} else {
len = 0;
}
268
269
270
271
272
if (!music_internal_playing()) {
music_internal_halt();
if (music_finished_hook) {
music_finished_hook();
273
}
274
275
}
}
276
277
}
278
279
/* Load the music interface libraries for a given music type */
SDL_bool load_music_type(Mix_MusicType type)
280
{
281
282
size_t i;
int loaded = 0;
283
284
for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
Mix_MusicInterface *interface = s_music_interfaces[i];
285
if (interface->type != type) {
286
287
continue;
}
288
289
290
291
292
293
if (!interface->loaded) {
char hint[64];
SDL_snprintf(hint, sizeof(hint), "SDL_MIXER_DISABLE_%s", interface->tag);
if (SDL_GetHintBoolean(hint, SDL_FALSE)) {
continue;
}
294
295
296
297
298
299
300
if (interface->Load && interface->Load() < 0) {
if (SDL_GetHintBoolean(SDL_MIXER_HINT_DEBUG_MUSIC_INTERFACES, SDL_FALSE)) {
SDL_Log("Couldn't load %s: %s\n", interface->tag, Mix_GetError());
}
continue;
}
301
302
interface->loaded = SDL_TRUE;
}
303
++loaded;
304
}
305
return (loaded > 0) ? SDL_TRUE : SDL_FALSE;
306
307
}
308
309
/* Open the music interfaces for a given music type */
SDL_bool open_music_type(Mix_MusicType type)
310
{
311
312
size_t i;
int opened = 0;
313
SDL_bool use_native_midi = SDL_FALSE;
314
315
316
317
if (!music_spec.format) {
/* Music isn't opened yet */
return SDL_FALSE;
318
319
}
320
#ifdef MUSIC_MID_NATIVE
321
if (type == MUS_MID && SDL_GetHintBoolean("SDL_NATIVE_MUSIC", SDL_FALSE) && native_midi_detect()) {
322
use_native_midi = SDL_TRUE;
323
}
324
#endif
325
326
327
328
329
330
for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
Mix_MusicInterface *interface = s_music_interfaces[i];
if (!interface->loaded) {
continue;
}
331
332
333
if (type != MUS_NONE && interface->type != type) {
continue;
}
334
335
336
337
338
if (interface->type == MUS_MID && use_native_midi && interface->api != MIX_MUSIC_NATIVEMIDI) {
continue;
}
339
340
341
342
343
344
345
if (!interface->opened) {
if (interface->Open && interface->Open(&music_spec) < 0) {
if (SDL_GetHintBoolean(SDL_MIXER_HINT_DEBUG_MUSIC_INTERFACES, SDL_FALSE)) {
SDL_Log("Couldn't open %s: %s\n", interface->tag, Mix_GetError());
}
continue;
}
346
347
348
interface->opened = SDL_TRUE;
add_music_decoder(interface->tag);
}
349
++opened;
350
351
}
352
353
if (has_music(MUS_MOD)) {
add_music_decoder("MOD");
354
add_chunk_decoder("MOD");
355
356
357
}
if (has_music(MUS_MID)) {
add_music_decoder("MIDI");
358
359
360
361
362
add_chunk_decoder("MID");
}
if (has_music(MUS_OGG)) {
add_music_decoder("OGG");
add_chunk_decoder("OGG");
363
}
364
365
366
367
if (has_music(MUS_OPUS)) {
add_music_decoder("OPUS");
add_chunk_decoder("OPUS");
}
368
369
if (has_music(MUS_MP3)) {
add_music_decoder("MP3");
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
add_chunk_decoder("MP3");
}
if (has_music(MUS_FLAC)) {
add_music_decoder("FLAC");
add_chunk_decoder("FLAC");
}
return (opened > 0) ? SDL_TRUE : SDL_FALSE;
}
/* Initialize the music interfaces with a certain desired audio format */
void open_music(const SDL_AudioSpec *spec)
{
#ifdef MIX_INIT_SOUNDFONT_PATHS
if (!soundfont_paths) {
soundfont_paths = SDL_strdup(MIX_INIT_SOUNDFONT_PATHS);
386
}
387
388
389
390
391
392
393
394
395
#endif
/* Load the music interfaces that don't have explicit initialization */
load_music_type(MUS_CMD);
load_music_type(MUS_WAV);
/* Open all the interfaces that are loaded */
music_spec = *spec;
open_music_type(MUS_NONE);
396
397
398
399
Mix_VolumeMusic(MIX_MAX_VOLUME);
/* Calculate the number of ms for each callback */
400
ms_per_step = (int) (((float)spec->samples * 1000.0f) / spec->freq);
401
}
402
403
404
405
/* Return SDL_TRUE if the music type is available */
SDL_bool has_music(Mix_MusicType type)
{
406
size_t i;
407
408
409
410
411
412
413
414
415
416
for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
Mix_MusicInterface *interface = s_music_interfaces[i];
if (interface->type != type) {
continue;
}
if (interface->opened) {
return SDL_TRUE;
}
}
return SDL_FALSE;
417
418
}
419
Mix_MusicType detect_music_type(SDL_RWops *src)
420
{
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
Uint8 magic[12];
if (SDL_RWread(src, magic, 1, 12) != 12) {
Mix_SetError("Couldn't read first 12 bytes of audio data");
return MUS_NONE;
}
SDL_RWseek(src, -12, RW_SEEK_CUR);
/* WAVE files have the magic four bytes "RIFF"
AIFF files have the magic 12 bytes "FORM" XXXX "AIFF" */
if (((SDL_memcmp(magic, "RIFF", 4) == 0) && (SDL_memcmp((magic+8), "WAVE", 4) == 0)) ||
(SDL_memcmp(magic, "FORM", 4) == 0)) {
return MUS_WAV;
}
436
/* Ogg Vorbis files have the magic four bytes "OggS" */
437
if (SDL_memcmp(magic, "OggS", 4) == 0) {
438
439
SDL_RWseek(src, 28, RW_SEEK_CUR);
SDL_RWread(src, magic, 1, 8);
440
SDL_RWseek(src,-36, RW_SEEK_CUR);
441
442
443
if (SDL_memcmp(magic, "OpusHead", 8) == 0) {
return MUS_OPUS;
}
444
445
446
447
return MUS_OGG;
}
/* FLAC files have the magic four bytes "fLaC" */
448
if (SDL_memcmp(magic, "fLaC", 4) == 0) {
449
450
451
452
return MUS_FLAC;
}
/* MIDI files have the magic four bytes "MThd" */
453
if (SDL_memcmp(magic, "MThd", 4) == 0) {
454
455
456
return MUS_MID;
}
457
458
if (SDL_memcmp(magic, "ID3", 3) == 0 ||
(magic[0] == 0xFF && (magic[1] & 0xFE) == 0xFA)) {
459
460
461
462
463
464
465
466
467
return MUS_MP3;
}
/* Assume MOD format.
*
* Apparently there is no way to check if the file is really a MOD,
* or there are too many formats supported by MikMod/ModPlug, or
* MikMod/ModPlug does this check by itself. */
return MUS_MOD;
468
469
}
470
471
472
/* Load a music file */
Mix_Music *Mix_LoadMUS(const char *file)
{
473
size_t i;
474
475
void *context;
char *ext;
476
Mix_MusicType type;
477
478
479
480
481
482
SDL_RWops *src;
for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
Mix_MusicInterface *interface = s_music_interfaces[i];
if (!interface->opened || !interface->CreateFromFile) {
continue;
483
}
484
485
486
487
488
489
490
491
492
493
494
495
context = interface->CreateFromFile(file);
if (context) {
/* Allocate memory for the music structure */
Mix_Music *music = (Mix_Music *)SDL_calloc(1, sizeof(Mix_Music));
if (music == NULL) {
Mix_SetError("Out of memory");
return NULL;
}
music->interface = interface;
music->context = context;
return music;
496
497
498
}
}
499
src = SDL_RWFromFile(file, "rb");
500
if (src == NULL) {
501
502
503
504
505
506
Mix_SetError("Couldn't open '%s'", file);
return NULL;
}
/* Use the extension as a first guess on the file type */
type = MUS_NONE;
507
ext = SDL_strrchr(file, '.');
508
if (ext) {
509
++ext; /* skip the dot in the extension */
510
if (SDL_strcasecmp(ext, "WAV") == 0) {
511
type = MUS_WAV;
512
513
514
} else if (SDL_strcasecmp(ext, "MID") == 0 ||
SDL_strcasecmp(ext, "MIDI") == 0 ||
SDL_strcasecmp(ext, "KAR") == 0) {
515
type = MUS_MID;
516
} else if (SDL_strcasecmp(ext, "OGG") == 0) {
517
type = MUS_OGG;
518
519
} else if (SDL_strcasecmp(ext, "OPUS") == 0) {
type = MUS_OPUS;
520
} else if (SDL_strcasecmp(ext, "FLAC") == 0) {
521
type = MUS_FLAC;
522
523
524
525
} else if (SDL_strcasecmp(ext, "MPG") == 0 ||
SDL_strcasecmp(ext, "MPEG") == 0 ||
SDL_strcasecmp(ext, "MP3") == 0 ||
SDL_strcasecmp(ext, "MAD") == 0) {
526
type = MUS_MP3;
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
} else if (SDL_strcasecmp(ext, "669") == 0 ||
SDL_strcasecmp(ext, "AMF") == 0 ||
SDL_strcasecmp(ext, "AMS") == 0 ||
SDL_strcasecmp(ext, "DBM") == 0 ||
SDL_strcasecmp(ext, "DSM") == 0 ||
SDL_strcasecmp(ext, "FAR") == 0 ||
SDL_strcasecmp(ext, "IT") == 0 ||
SDL_strcasecmp(ext, "MED") == 0 ||
SDL_strcasecmp(ext, "MDL") == 0 ||
SDL_strcasecmp(ext, "MOD") == 0 ||
SDL_strcasecmp(ext, "MOL") == 0 ||
SDL_strcasecmp(ext, "MTM") == 0 ||
SDL_strcasecmp(ext, "NST") == 0 ||
SDL_strcasecmp(ext, "OKT") == 0 ||
SDL_strcasecmp(ext, "PTM") == 0 ||
SDL_strcasecmp(ext, "S3M") == 0 ||
SDL_strcasecmp(ext, "STM") == 0 ||
SDL_strcasecmp(ext, "ULT") == 0 ||
SDL_strcasecmp(ext, "UMX") == 0 ||
SDL_strcasecmp(ext, "WOW") == 0 ||
SDL_strcasecmp(ext, "XM") == 0) {
type = MUS_MOD;
549
550
}
}
551
return Mix_LoadMUSType_RW(src, type, SDL_TRUE);
552
553
}
554
Mix_Music *Mix_LoadMUS_RW(SDL_RWops *src, int freesrc)
555
{
556
return Mix_LoadMUSType_RW(src, MUS_NONE, freesrc);
557
558
}
559
Mix_Music *Mix_LoadMUSType_RW(SDL_RWops *src, Mix_MusicType type, int freesrc)
560
{
561
size_t i;
562
void *context;
563
Sint64 start;
564
565
if (!src) {
566
567
568
Mix_SetError("RWops pointer is NULL");
return NULL;
}
569
start = SDL_RWtell(src);
570
571
572
573
/* If the caller wants auto-detection, figure out what kind of file
* this is. */
if (type == MUS_NONE) {
574
if ((type = detect_music_type(src)) == MUS_NONE) {
575
/* Don't call Mix_SetError() since detect_music_type() does that. */
576
577
578
if (freesrc) {
SDL_RWclose(src);
}
579
580
581
582
return NULL;
}
}
583
Mix_ClearError();
584
585
586
587
588
589
590
if (load_music_type(type) && open_music_type(type)) {
for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) {
Mix_MusicInterface *interface = s_music_interfaces[i];
if (!interface->opened || type != interface->type || !interface->CreateFromRW) {
continue;
}
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
context = interface->CreateFromRW(src, freesrc);
if (context) {
/* Allocate memory for the music structure */
Mix_Music *music = (Mix_Music *)SDL_calloc(1, sizeof(Mix_Music));
if (music == NULL) {
interface->Delete(context);
Mix_SetError("Out of memory");
return NULL;
}
music->interface = interface;
music->context = context;
if (SDL_GetHintBoolean(SDL_MIXER_HINT_DEBUG_MUSIC_INTERFACES, SDL_FALSE)) {
SDL_Log("Loaded music with %s\n", interface->tag);
}
return music;
608
}
609
610
611
612
/* Reset the stream for the next decoder */
SDL_RWseek(src, start, RW_SEEK_SET);
}
613
}
614
615
616
if (!*Mix_GetError()) {
Mix_SetError("Unrecognized audio format");
617
}
618
619
620
621
622
623
if (freesrc) {
SDL_RWclose(src);
} else {
SDL_RWseek(src, start, RW_SEEK_SET);
}
return NULL;
624
625
626
627
628
}
/* Free a music chunk previously loaded */
void Mix_FreeMusic(Mix_Music *music)
{
629
if (music) {
630
/* Stop the music if it's currently playing */
631
Mix_LockAudio();
632
if (music == music_playing) {
633
/* Wait for any fade out to finish */
634
while (music->fading == MIX_FADING_OUT) {
635
Mix_UnlockAudio();
636
SDL_Delay(100);
637
Mix_LockAudio();
638
}
639
if (music == music_playing) {
640
641
642
music_internal_halt();
}
}
643
Mix_UnlockAudio();
644
645
music->interface->Delete(music->context);
646
647
SDL_free(music);
}
648
649
}
650
651
652
653
654
/* Find out the music format of a mixer music, or the currently playing
music, if 'music' is NULL.
*/
Mix_MusicType Mix_GetMusicType(const Mix_Music *music)
{
655
656
Mix_MusicType type = MUS_NONE;
657
658
if (music) {
type = music->interface->type;
659
} else {
660
Mix_LockAudio();
661
662
if (music_playing) {
type = music_playing->interface->type;
663
}
664
Mix_UnlockAudio();
665
666
}
return(type);
667
668
}
669
670
/* Play a music chunk. Returns 0, or -1 if there was an error.
*/
671
static int music_internal_play(Mix_Music *music, int play_count, double position)
672
{
673
int retval = 0;
674
675
#if defined(__MACOSX__) && defined(MID_MUSIC_NATIVE)
676
677
678
/* This fixes a bug with native MIDI on Mac OS X, where you
can't really stop and restart MIDI from the audio callback.
*/
679
if (music == music_playing && music->api == MIX_MUSIC_NATIVEMIDI) {
680
681
682
683
684
685
686
/* Just a seek suffices to restart playing */
music_internal_position(position);
return 0;
}
#endif
/* Note the music we're playing */
687
if (music_playing) {
688
689
690
music_internal_halt();
}
music_playing = music;
691
music_playing->playing = SDL_TRUE;
692
693
/* Set the initial volume */
694
music_internal_initialize_volume();
695
696
/* Set up for playback */
697
retval = music->interface->Play(music->context, play_count);
698
699
/* Set the playback position, note any errors if an offset is used */
700
701
702
if (retval == 0) {
if (position > 0.0) {
if (music_internal_position(position) < 0) {
703
704
705
706
707
708
709
710
711
Mix_SetError("Position not implemented for music type");
retval = -1;
}
} else {
music_internal_position(0.0);
}
}
/* If the setup failed, we're not playing any music anymore */
712
if (retval < 0) {
713
music->playing = SDL_FALSE;
714
715
716
music_playing = NULL;
}
return(retval);
717
}
718
719
int Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position)
720
{
721
722
int retval;
723
if (ms_per_step == 0) {
724
725
726
727
728
SDL_SetError("Audio device hasn't been opened");
return(-1);
}
/* Don't play null pointers :-) */
729
if (music == NULL) {
730
731
732
733
734
Mix_SetError("music parameter was NULL");
return(-1);
}
/* Setup the data */
735
if (ms) {
736
737
738
739
740
741
742
743
music->fading = MIX_FADING_IN;
} else {
music->fading = MIX_NO_FADING;
}
music->fade_step = 0;
music->fade_steps = ms/ms_per_step;
/* Play the puppy */
744
Mix_LockAudio();
745
/* If the current music is fading out, wait for the fade to complete */
746
while (music_playing && (music_playing->fading == MIX_FADING_OUT)) {
747
Mix_UnlockAudio();
748
SDL_Delay(100);
749
Mix_LockAudio();
750
}
751
if (loops == 0) {
752
/* Loop is the number of times to play the audio */
753
loops = 1;
754
}
755
retval = music_internal_play(music, loops, position);
756
/* Set music as active */
757
music_active = (retval == 0);
758
Mix_UnlockAudio();
759
760
return(retval);
761
762
763
}
int Mix_FadeInMusic(Mix_Music *music, int loops, int ms)
{
764
return Mix_FadeInMusicPos(music, loops, ms, 0.0);
765
766
767
}
int Mix_PlayMusic(Mix_Music *music, int loops)
{
768
return Mix_FadeInMusicPos(music, loops, 0, 0.0);
769
770
}
771
772
/* Set the playing music position */
int music_internal_position(double position)
773
{
774
775
if (music_playing->interface->Seek) {
return music_playing->interface->Seek(music_playing->context, position);
776
}
777
return -1;
778
}
779
int Mix_SetMusicPosition(double position)
780
{
781
782
int retval;
783
Mix_LockAudio();
784
if (music_playing) {
785
retval = music_internal_position(position);
786
if (retval < 0) {
787
788
789
790
791
792
Mix_SetError("Position not implemented for music type");
}
} else {
Mix_SetError("Music isn't playing");
retval = -1;
}
793
Mix_UnlockAudio();
794
795
return(retval);
796
797
}
798
static double music_internal_duration(Mix_Music *music)
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
{
if (music->interface->Duration) {
return music->interface->Duration(music->context);
} else {
Mix_SetError("Duration not implemented for music type");
return -1;
}
}
double Mix_MusicDuration(Mix_Music *music)
{
double retval;
Mix_LockAudio();
if (music) {
814
retval = music_internal_duration(music);
815
} else if (music_playing) {
816
retval = music_internal_duration(music_playing);
817
818
819
820
821
822
823
824
825
} else {
Mix_SetError("music is NULL and no playing music");
retval = -1;
}
Mix_UnlockAudio();
return(retval);
}
826
827
828
/* Set the music's initial volume */
static void music_internal_initialize_volume(void)
{
829
if (music_playing->fading == MIX_FADING_IN) {
830
831
832
833
music_internal_volume(0);
} else {
music_internal_volume(music_volume);
}
834
835
}
836
/* Set the music volume */
837
static void music_internal_volume(int volume)
838
{
839
840
if (music_playing->interface->SetVolume) {
music_playing->interface->SetVolume(music_playing->context, volume);
841
}
842
843
844
}
int Mix_VolumeMusic(int volume)
{
845
846
847
int prev_volume;
prev_volume = music_volume;
848
if (volume < 0) {
849
850
return prev_volume;
}
851
if (volume > SDL_MIX_MAXVOLUME) {
852
853
854
volume = SDL_MIX_MAXVOLUME;
}
music_volume = volume;
855
Mix_LockAudio();
856
if (music_playing) {
857
858
music_internal_volume(music_volume);
}
859
Mix_UnlockAudio();
860
return(prev_volume);
861
862
}
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
int Mix_GetVolumeMusicStream(Mix_Music *music)
{
int prev_volume;
if (music && music->interface->GetVolume)
prev_volume = music->interface->GetVolume(music->context);
else if (music_playing && music_playing->interface->GetVolume) {
prev_volume = music_playing->interface->GetVolume(music_playing->context);
} else {
prev_volume = music_volume;
}
return prev_volume;
}
878
879
/* Halt playing of music */
static void music_internal_halt(void)
880
{
881
882
if (music_playing->interface->Stop) {
music_playing->interface->Stop(music_playing->context);
883
}
884
885
music_playing->playing = SDL_FALSE;
886
887
music_playing->fading = MIX_NO_FADING;
music_playing = NULL;
888
889
890
}
int Mix_HaltMusic(void)
{
891
Mix_LockAudio();
892
if (music_playing) {
893
music_internal_halt();
894
if (music_finished_hook) {
895
896
897
music_finished_hook();
}
}
898
Mix_UnlockAudio();
899
900
return(0);
901
902
}
903
904
905
/* Progressively stop the music */
int Mix_FadeOutMusic(int ms)
{
906
int retval = 0;
907
908
if (ms_per_step == 0) {
909
910
911
SDL_SetError("Audio device hasn't been opened");
return 0;
}
912
913
914
915
916
if (ms <= 0) { /* just halt immediately. */
Mix_HaltMusic();
return 1;
}
917
918
Mix_LockAudio();
919
920
921
922
923
924
925
926
927
928
929
930
931
932
if (music_playing) {
int fade_steps = (ms + ms_per_step - 1) / ms_per_step;
if (music_playing->fading == MIX_NO_FADING) {
music_playing->fade_step = 0;
} else {
int step;
int old_fade_steps = music_playing->fade_steps;
if (music_playing->fading == MIX_FADING_OUT) {
step = music_playing->fade_step;
} else {
step = old_fade_steps - music_playing->fade_step + 1;
}
music_playing->fade_step = (step * fade_steps) / old_fade_steps;
}
933
934
935
936
music_playing->fading = MIX_FADING_OUT;
music_playing->fade_steps = fade_steps;
retval = 1;
}
937
Mix_UnlockAudio();
938
939
return(retval);
940
941
942
943
}
Mix_Fading Mix_FadingMusic(void)
{
944
Mix_Fading fading = MIX_NO_FADING;
945
946
Mix_LockAudio();
947
if (music_playing) {
948
949
fading = music_playing->fading;
}
950
Mix_UnlockAudio();
951
952
return(fading);
953
954
}
955
956
957
/* Pause/Resume the music stream */
void Mix_PauseMusic(void)
{
958
Mix_LockAudio();
959
960
961
962
if (music_playing) {
if (music_playing->interface->Pause) {
music_playing->interface->Pause(music_playing->context);
}
963
}
964
music_active = SDL_FALSE;
965
Mix_UnlockAudio();
966
}
967
968
969
void Mix_ResumeMusic(void)
{
970
Mix_LockAudio();
971
972
973
974
if (music_playing) {
if (music_playing->interface->Resume) {
music_playing->interface->Resume(music_playing->context);
}
975
}
976
music_active = SDL_TRUE;
977
Mix_UnlockAudio();
978
979
980
981
}
void Mix_RewindMusic(void)
{
982
Mix_SetMusicPosition(0.0);
983
984
}
985
986
int Mix_PausedMusic(void)
{
987
return (music_active == SDL_FALSE);
988
989
}
990
/* Check the status of the music */
991
static SDL_bool music_internal_playing(void)
992
{
993
if (!music_playing) {
994
return SDL_FALSE;
995
}
996
997
if (music_playing->interface->IsPlaying) {
998
music_playing->playing = music_playing->interface->IsPlaying(music_playing->context);
999
}
1000
return music_playing->playing;