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