/
mixer.c
1610 lines (1403 loc) · 44.4 KB
1
/*
2
SDL_mixer: An audio mixer library based on the SDL library
3
Copyright (C) 1997-2018 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
*/
22
/* $Id$ */
23
24
25
26
27
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
28
#include "SDL.h"
29
30
#include "SDL_mixer.h"
31
#include "mixer.h"
32
#include "music.h"
33
34
35
#include "load_aiff.h"
#include "load_voc.h"
36
37
38
#define __MIX_INTERNAL_EFFECT__
#include "effects_internal.h"
39
/* Magic numbers for various audio file formats */
40
41
42
43
#define RIFF 0x46464952 /* "RIFF" */
#define WAVE 0x45564157 /* "WAVE" */
#define FORM 0x4d524f46 /* "FORM" */
#define CREA 0x61657243 /* "Crea" */
44
45
46
static int audio_opened = 0;
static SDL_AudioSpec mixer;
47
static SDL_AudioDeviceID audio_device;
48
49
50
typedef struct _Mix_effectinfo
{
51
52
53
54
Mix_EffectFunc_t callback;
Mix_EffectDone_t done_callback;
void *udata;
struct _Mix_effectinfo *next;
55
56
} effect_info;
57
static struct _Mix_Channel {
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
Mix_Chunk *chunk;
int playing;
int paused;
Uint8 *samples;
int volume;
int looping;
int tag;
Uint32 expire;
Uint32 start_time;
Mix_Fading fading;
int fade_volume;
int fade_volume_reset;
Uint32 fade_length;
Uint32 ticks_fade;
effect_info *effects;
73
} *mix_channel = NULL;
74
75
76
static effect_info *posteffects = NULL;
77
static int num_channels;
78
static int reserved_channels = 0;
79
80
81
/* Support for hooking into the mixer callback system */
82
static void (SDLCALL *mix_postmix)(void *udata, Uint8 *stream, int len) = NULL;
83
static void *mix_postmix_data = NULL;
84
85
/* rcg07062001 callback to alert when channels are done playing. */
86
static void (SDLCALL *channel_done_callback)(int channel) = NULL;
87
88
/* Support for user defined music functions */
89
static void (SDLCALL *mix_music)(void *udata, Uint8 *stream, int len) = music_mixer;
90
static void *music_data = NULL;
91
92
93
94
95
/* rcg06042009 report available decoders at runtime. */
static const char **chunk_decoders = NULL;
static int num_decoders = 0;
96
97
int Mix_GetNumChunkDecoders(void)
98
{
99
return(num_decoders);
100
101
102
103
}
const char *Mix_GetChunkDecoder(int index)
{
104
105
106
107
if ((index < 0) || (index >= num_decoders)) {
return NULL;
}
return(chunk_decoders[index]);
108
109
}
110
111
112
113
114
115
116
117
118
119
120
SDL_bool Mix_HasChunkDecoder(const char *name)
{
int index;
for (index = 0; index < num_decoders; ++index) {
if (SDL_strcasecmp(name, chunk_decoders[index]) == 0) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
121
void add_chunk_decoder(const char *decoder)
122
{
123
124
125
126
127
128
129
130
131
132
133
int i;
void *ptr;
/* Check to see if we already have this decoder */
for (i = 0; i < num_decoders; ++i) {
if (SDL_strcmp(chunk_decoders[i], decoder) == 0) {
return;
}
}
ptr = SDL_realloc((void *)chunk_decoders, (num_decoders + 1) * sizeof (const char *));
134
135
136
137
138
if (ptr == NULL) {
return; /* oh well, go on without it. */
}
chunk_decoders = (const char **) ptr;
chunk_decoders[num_decoders++] = decoder;
139
}
140
141
142
143
/* rcg06192001 get linked library's version. */
const SDL_version *Mix_Linked_Version(void)
{
144
145
146
static SDL_version linked_version;
SDL_MIXER_VERSION(&linked_version);
return(&linked_version);
147
148
}
149
150
int Mix_Init(int flags)
{
151
int result = 0;
152
153
if (flags & MIX_INIT_FLAC) {
154
155
if (load_music_type(MUS_FLAC)) {
open_music_type(MUS_FLAC);
156
result |= MIX_INIT_FLAC;
157
158
} else {
Mix_SetError("FLAC support not available");
159
160
161
}
}
if (flags & MIX_INIT_MOD) {
162
163
if (load_music_type(MUS_MOD)) {
open_music_type(MUS_MOD);
164
result |= MIX_INIT_MOD;
165
166
} else {
Mix_SetError("MOD support not available");
167
168
169
}
}
if (flags & MIX_INIT_MP3) {
170
171
if (load_music_type(MUS_MP3)) {
open_music_type(MUS_MP3);
172
result |= MIX_INIT_MP3;
173
174
} else {
Mix_SetError("MP3 support not available");
175
176
177
}
}
if (flags & MIX_INIT_OGG) {
178
179
if (load_music_type(MUS_OGG)) {
open_music_type(MUS_OGG);
180
result |= MIX_INIT_OGG;
181
182
183
184
} else {
Mix_SetError("OGG support not available");
}
}
185
186
187
188
189
190
191
192
if (flags & MIX_INIT_OPUS) {
if (load_music_type(MUS_OPUS)) {
open_music_type(MUS_OPUS);
result |= MIX_INIT_OPUS;
} else {
Mix_SetError("OPUS support not available");
}
}
193
if (flags & MIX_INIT_MID) {
194
195
if (load_music_type(MUS_MID)) {
open_music_type(MUS_MID);
196
197
198
result |= MIX_INIT_MID;
} else {
Mix_SetError("MIDI support not available");
199
200
}
}
201
return result;
202
203
204
205
}
void Mix_Quit()
{
206
unload_music();
207
208
}
209
static int _Mix_remove_all_effects(int channel, effect_info **e);
210
211
212
/*
* rcg06122001 Cleanup effect callbacks.
213
* MAKE SURE Mix_LockAudio() is called before this (or you're in the
214
215
216
* audio callback).
*/
static void _Mix_channel_done_playing(int channel)
217
{
218
219
220
221
222
223
224
225
226
if (channel_done_callback) {
channel_done_callback(channel);
}
/*
* Call internal function directly, to avoid locking audio from
* inside audio callback.
*/
_Mix_remove_all_effects(channel, &mix_channel[channel].effects);
227
228
229
230
231
}
static void *Mix_DoEffects(int chan, void *snd, int len)
{
232
233
234
235
236
237
238
239
240
241
242
int posteffect = (chan == MIX_CHANNEL_POST);
effect_info *e = ((posteffect) ? posteffects : mix_channel[chan].effects);
void *buf = snd;
if (e != NULL) { /* are there any registered effects? */
/* if this is the postmix, we can just overwrite the original. */
if (!posteffect) {
buf = SDL_malloc(len);
if (buf == NULL) {
return(snd);
}
243
SDL_memcpy(buf, snd, len);
244
245
246
247
248
249
250
251
252
253
254
}
for (; e != NULL; e = e->next) {
if (e->callback != NULL) {
e->callback(chan, buf, len, e->udata);
}
}
}
/* be sure to SDL_free() the return value if != snd ... */
return(buf);
255
256
257
}
258
/* Mixing function */
259
260
static void SDLCALL
mix_channels(void *udata, Uint8 *stream, int len)
261
{
262
Uint8 *mix_input;
263
int i, mixable, volume = MIX_MAX_VOLUME;
264
Uint32 sdl_ticks;
265
266
#if SDL_VERSION_ATLEAST(1, 3, 0)
267
/* Need to initialize the stream in SDL 1.3+ */
268
SDL_memset(stream, mixer.silence, len);
269
#endif
270
271
/* Mix the music (must be done before the channels are added) */
272
mix_music(music_data, stream, len);
273
274
275
/* Mix any playing channels... */
sdl_ticks = SDL_GetTicks();
276
277
278
for (i=0; i<num_channels; ++i) {
if (!mix_channel[i].paused) {
if (mix_channel[i].expire > 0 && mix_channel[i].expire < sdl_ticks) {
279
280
281
282
283
284
/* Expiration delay for that channel is reached */
mix_channel[i].playing = 0;
mix_channel[i].looping = 0;
mix_channel[i].fading = MIX_NO_FADING;
mix_channel[i].expire = 0;
_Mix_channel_done_playing(i);
285
} else if (mix_channel[i].fading != MIX_NO_FADING) {
286
Uint32 ticks = sdl_ticks - mix_channel[i].ticks_fade;
287
if (ticks >= mix_channel[i].fade_length) {
288
Mix_Volume(i, mix_channel[i].fade_volume_reset); /* Restore the volume */
289
if(mix_channel[i].fading == MIX_FADING_OUT) {
290
291
292
293
294
295
296
mix_channel[i].playing = 0;
mix_channel[i].looping = 0;
mix_channel[i].expire = 0;
_Mix_channel_done_playing(i);
}
mix_channel[i].fading = MIX_NO_FADING;
} else {
297
if (mix_channel[i].fading == MIX_FADING_OUT) {
298
Mix_Volume(i, (mix_channel[i].fade_volume * (mix_channel[i].fade_length-ticks))
299
/ mix_channel[i].fade_length);
300
} else {
301
Mix_Volume(i, (mix_channel[i].fade_volume * ticks) / mix_channel[i].fade_length);
302
303
304
}
}
}
305
if (mix_channel[i].playing > 0) {
306
307
308
309
310
311
int index = 0;
int remaining = len;
while (mix_channel[i].playing > 0 && index < len) {
remaining = len - index;
volume = (mix_channel[i].volume*mix_channel[i].chunk->volume) / MIX_MAX_VOLUME;
mixable = mix_channel[i].playing;
312
if (mixable > remaining) {
313
314
315
316
mixable = remaining;
}
mix_input = Mix_DoEffects(i, mix_channel[i].samples, mixable);
317
SDL_MixAudioFormat(stream+index,mix_input,mixer.format,mixable,volume);
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
if (mix_input != mix_channel[i].samples)
SDL_free(mix_input);
mix_channel[i].samples += mixable;
mix_channel[i].playing -= mixable;
index += mixable;
/* rcg06072001 Alert app if channel is done playing. */
if (!mix_channel[i].playing && !mix_channel[i].looping) {
_Mix_channel_done_playing(i);
}
}
/* If looping the sample and we are at its end, make sure
we will still return a full buffer */
333
while (mix_channel[i].looping && index < len) {
334
335
336
337
338
339
340
int alen = mix_channel[i].chunk->alen;
remaining = len - index;
if (remaining > alen) {
remaining = alen;
}
mix_input = Mix_DoEffects(i, mix_channel[i].chunk->abuf, remaining);
341
SDL_MixAudioFormat(stream+index, mix_input, mixer.format, remaining, volume);
342
343
344
if (mix_input != mix_channel[i].chunk->abuf)
SDL_free(mix_input);
345
346
347
if (mix_channel[i].looping > 0) {
--mix_channel[i].looping;
}
348
349
350
351
mix_channel[i].samples = mix_channel[i].chunk->abuf + remaining;
mix_channel[i].playing = mix_channel[i].chunk->alen - remaining;
index += remaining;
}
352
if (! mix_channel[i].playing && mix_channel[i].looping) {
353
354
355
if (mix_channel[i].looping > 0) {
--mix_channel[i].looping;
}
356
357
358
359
360
361
362
363
364
365
mix_channel[i].samples = mix_channel[i].chunk->abuf;
mix_channel[i].playing = mix_channel[i].chunk->alen;
}
}
}
}
/* rcg06122001 run posteffects... */
Mix_DoEffects(MIX_CHANNEL_POST, stream, len);
366
if (mix_postmix) {
367
368
mix_postmix(mix_postmix_data, stream, len);
}
369
370
}
371
#if 0
372
373
static void PrintFormat(char *title, SDL_AudioSpec *fmt)
{
374
375
376
377
printf("%s: %d bit %s audio (%s) at %u Hz\n", title, (fmt->format&0xFF),
(fmt->format&0x8000) ? "signed" : "unsigned",
(fmt->channels > 2) ? "surround" :
(fmt->channels > 1) ? "stereo" : "mono", fmt->freq);
378
}
379
#endif
380
381
/* Open the mixer with a certain desired audio format */
382
int Mix_OpenAudioDevice(int frequency, Uint16 format, int nchannels, int chunksize,
383
const char* device, int allowed_changes)
384
{
385
386
387
int i;
SDL_AudioSpec desired;
388
389
390
391
392
393
394
395
396
/* This used to call SDL_OpenAudio(), which initializes the audio
subsystem if necessary. Since SDL_OpenAudioDevice() doesn't,
we have to handle this case here. */
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
return -1;
}
}
397
/* If the mixer is already opened, increment open count */
398
399
if (audio_opened) {
if (format == mixer.format && nchannels == mixer.channels) {
400
401
402
++audio_opened;
return(0);
}
403
while (audio_opened) {
404
405
406
407
408
409
410
411
412
413
414
415
416
Mix_CloseAudio();
}
}
/* Set the desired format and frequency */
desired.freq = frequency;
desired.format = format;
desired.channels = nchannels;
desired.samples = chunksize;
desired.callback = mix_channels;
desired.userdata = NULL;
/* Accept nearly any audio format */
417
if ((audio_device = SDL_OpenAudioDevice(device, 0, &desired, &mixer, allowed_changes)) == 0) {
418
419
return(-1);
}
420
#if 0
421
PrintFormat("Audio device", &mixer);
422
423
#endif
424
425
426
427
num_channels = MIX_CHANNELS;
mix_channel = (struct _Mix_Channel *) SDL_malloc(num_channels * sizeof(struct _Mix_Channel));
/* Clear out the audio channels */
428
for (i=0; i<num_channels; ++i) {
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
mix_channel[i].chunk = NULL;
mix_channel[i].playing = 0;
mix_channel[i].looping = 0;
mix_channel[i].volume = SDL_MIX_MAXVOLUME;
mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME;
mix_channel[i].fade_volume_reset = SDL_MIX_MAXVOLUME;
mix_channel[i].fading = MIX_NO_FADING;
mix_channel[i].tag = -1;
mix_channel[i].expire = 0;
mix_channel[i].effects = NULL;
mix_channel[i].paused = 0;
}
Mix_VolumeMusic(SDL_MIX_MAXVOLUME);
_Mix_InitEffects();
add_chunk_decoder("WAVE");
add_chunk_decoder("AIFF");
add_chunk_decoder("VOC");
448
449
450
/* Initialize the music players */
open_music(&mixer);
451
452
audio_opened = 1;
453
SDL_PauseAudioDevice(audio_device, 0);
454
return(0);
455
456
}
457
458
459
/* Open the mixer with a certain desired audio format */
int Mix_OpenAudio(int frequency, Uint16 format, int nchannels, int chunksize)
{
460
461
462
return Mix_OpenAudioDevice(frequency, format, nchannels, chunksize, NULL,
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE |
SDL_AUDIO_ALLOW_CHANNELS_CHANGE);
463
464
}
465
466
467
468
469
470
/* Dynamically change the number of channels managed by the mixer.
If decreasing the number of channels, the upper channels are
stopped.
*/
int Mix_AllocateChannels(int numchans)
{
471
if (numchans<0 || numchans==num_channels)
472
473
return(num_channels);
474
if (numchans < num_channels) {
475
476
477
478
479
480
481
/* Stop the affected channels */
int i;
for(i=numchans; i < num_channels; i++) {
Mix_UnregisterAllEffects(i);
Mix_HaltChannel(i);
}
}
482
Mix_LockAudio();
483
mix_channel = (struct _Mix_Channel *) SDL_realloc(mix_channel, numchans * sizeof(struct _Mix_Channel));
484
if (numchans > num_channels) {
485
486
487
488
489
490
/* Initialize the new channels */
int i;
for(i=num_channels; i < numchans; i++) {
mix_channel[i].chunk = NULL;
mix_channel[i].playing = 0;
mix_channel[i].looping = 0;
491
492
493
mix_channel[i].volume = MIX_MAX_VOLUME;
mix_channel[i].fade_volume = MIX_MAX_VOLUME;
mix_channel[i].fade_volume_reset = MIX_MAX_VOLUME;
494
495
496
497
498
499
500
501
mix_channel[i].fading = MIX_NO_FADING;
mix_channel[i].tag = -1;
mix_channel[i].expire = 0;
mix_channel[i].effects = NULL;
mix_channel[i].paused = 0;
}
}
num_channels = numchans;
502
Mix_UnlockAudio();
503
return(num_channels);
504
505
}
506
507
508
/* Return the actual mixer parameters */
int Mix_QuerySpec(int *frequency, Uint16 *format, int *channels)
{
509
510
if (audio_opened) {
if (frequency) {
511
512
*frequency = mixer.freq;
}
513
if (format) {
514
515
*format = mixer.format;
}
516
if (channels) {
517
518
519
520
*channels = mixer.channels;
}
}
return(audio_opened);
521
522
}
523
typedef struct _MusicFragment
524
{
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
Uint8 *data;
int size;
struct _MusicFragment *next;
} MusicFragment;
static SDL_AudioSpec *Mix_LoadMusic_RW(Mix_MusicType music_type, SDL_RWops *src, int freesrc, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
{
int i;
Mix_MusicInterface *interface = NULL;
void *music = NULL;
Sint64 start;
SDL_bool playing;
MusicFragment *first = NULL, *last = NULL, *fragment = NULL;
int count = 0;
int fragment_size;
541
542
543
544
if (!load_music_type(music_type) || !open_music_type(music_type)) {
return NULL;
}
545
546
547
548
549
550
551
552
*spec = mixer;
/* Use fragments sized on full audio frame boundaries - this'll do */
fragment_size = spec->size;
start = SDL_RWtell(src);
for (i = 0; i < get_num_music_interfaces(); ++i) {
interface = get_music_interface(i);
553
554
555
if (!interface->opened) {
continue;
}
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
if (interface->type != music_type) {
continue;
}
if (!interface->CreateFromRW || !interface->GetAudio) {
continue;
}
/* These music interfaces are not safe to use while music is playing */
if (interface->api == MIX_MUSIC_CMD ||
interface->api == MIX_MUSIC_MIKMOD ||
interface->api == MIX_MUSIC_NATIVEMIDI) {
continue;
}
music = interface->CreateFromRW(src, freesrc);
if (music) {
/* The interface owns the data source now */
freesrc = SDL_FALSE;
break;
}
/* Reset the stream for the next decoder */
SDL_RWseek(src, start, RW_SEEK_SET);
579
580
}
581
582
583
584
585
586
587
588
589
590
591
if (!music) {
if (freesrc) {
SDL_RWclose(src);
}
Mix_SetError("Unrecognized audio format");
return NULL;
}
Mix_LockAudio();
if (interface->Play) {
592
interface->Play(music, 1);
593
}
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
playing = SDL_TRUE;
while (playing) {
int left;
fragment = (MusicFragment *)SDL_malloc(sizeof(*fragment));
if (!fragment) {
/* Uh oh, out of memory, let's return what we have */
break;
}
fragment->data = (Uint8 *)SDL_malloc(fragment_size);
if (!fragment->data) {
/* Uh oh, out of memory, let's return what we have */
SDL_free(fragment);
break;
}
fragment->next = NULL;
left = interface->GetAudio(music, fragment->data, fragment_size);
if (left > 0) {
playing = SDL_FALSE;
} else if (interface->IsPlaying) {
playing = interface->IsPlaying(music);
}
fragment->size = (fragment_size - left);
if (!first) {
first = fragment;
}
if (last) {
last->next = fragment;
}
last = fragment;
++count;
}
if (interface->Stop) {
interface->Stop(music);
}
if (music) {
interface->Delete(music);
636
}
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
Mix_UnlockAudio();
if (count > 0) {
*audio_len = (count - 1) * fragment_size + fragment->size;
*audio_buf = (Uint8 *)SDL_malloc(*audio_len);
if (*audio_buf) {
Uint8 *dst = *audio_buf;
for (fragment = first; fragment; fragment = fragment->next) {
SDL_memcpy(dst, fragment->data, fragment->size);
dst += fragment->size;
}
} else {
SDL_OutOfMemory();
spec = NULL;
}
} else {
Mix_SetError("No audio data");
spec = NULL;
}
while (first) {
fragment = first;
first = first->next;
SDL_free(fragment->data);
SDL_free(fragment);
}
if (freesrc) {
SDL_RWclose(src);
}
return spec;
669
}
670
671
672
673
/* Load a wave file */
Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc)
{
674
Uint8 magic[4];
675
676
677
678
679
680
Mix_Chunk *chunk;
SDL_AudioSpec wavespec, *loaded;
SDL_AudioCVT wavecvt;
int samplesize;
/* rcg06012001 Make sure src is valid */
681
if (!src) {
682
683
684
685
686
SDL_SetError("Mix_LoadWAV_RW with NULL src");
return(NULL);
}
/* Make sure audio has been opened */
687
if (!audio_opened) {
688
SDL_SetError("Audio device hasn't been opened");
689
if (freesrc) {
690
691
692
693
694
695
696
SDL_RWclose(src);
}
return(NULL);
}
/* Allocate the chunk memory */
chunk = (Mix_Chunk *)SDL_malloc(sizeof(Mix_Chunk));
697
if (chunk == NULL) {
698
SDL_SetError("Out of memory");
699
if (freesrc) {
700
701
702
703
704
705
SDL_RWclose(src);
}
return(NULL);
}
/* Find out what kind of audio file this is */
706
707
708
709
710
711
712
if (SDL_RWread(src, magic, 1, 4) != 4) {
if (freesrc) {
SDL_RWclose(src);
}
Mix_SetError("Couldn't read first 4 bytes of audio data");
return NULL;
}
713
/* Seek backwards for compatibility with older loaders */
714
715
716
717
718
719
SDL_RWseek(src, -4, RW_SEEK_CUR);
if (SDL_memcmp(magic, "WAVE", 4) == 0 || SDL_memcmp(magic, "RIFF", 4) == 0) {
loaded = SDL_LoadWAV_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
} else if (SDL_memcmp(magic, "FORM", 4) == 0) {
loaded = Mix_LoadAIFF_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
720
} else if (SDL_memcmp(magic, "Crea", 4) == 0) {
721
722
723
724
loaded = Mix_LoadVOC_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
} else {
Mix_MusicType music_type = detect_music_type_from_magic(magic);
loaded = Mix_LoadMusic_RW(music_type, src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
725
}
726
if (!loaded) {
727
728
729
730
/* The individual loaders have closed src if needed */
SDL_free(chunk);
return(NULL);
}
731
732
#if 0
733
734
PrintFormat("Audio device", &mixer);
PrintFormat("-- Wave file", &wavespec);
735
736
#endif
737
/* Build the audio converter and create conversion buffers */
738
if (wavespec.format != mixer.format ||
739
wavespec.channels != mixer.channels ||
740
741
wavespec.freq != mixer.freq) {
if (SDL_BuildAudioCVT(&wavecvt,
742
wavespec.format, wavespec.channels, wavespec.freq,
743
mixer.format, mixer.channels, mixer.freq) < 0) {
744
745
746
747
748
749
750
SDL_free(chunk->abuf);
SDL_free(chunk);
return(NULL);
}
samplesize = ((wavespec.format & 0xFF)/8)*wavespec.channels;
wavecvt.len = chunk->alen & ~(samplesize-1);
wavecvt.buf = (Uint8 *)SDL_calloc(1, wavecvt.len*wavecvt.len_mult);
751
if (wavecvt.buf == NULL) {
752
753
754
755
756
SDL_SetError("Out of memory");
SDL_free(chunk->abuf);
SDL_free(chunk);
return(NULL);
}
757
SDL_memcpy(wavecvt.buf, chunk->abuf, wavecvt.len);
758
759
760
SDL_free(chunk->abuf);
/* Run the audio converter */
761
if (SDL_ConvertAudio(&wavecvt) < 0) {
762
763
764
765
766
767
768
769
770
771
772
773
774
SDL_free(wavecvt.buf);
SDL_free(chunk);
return(NULL);
}
chunk->abuf = wavecvt.buf;
chunk->alen = wavecvt.len_cvt;
}
chunk->allocated = 1;
chunk->volume = MIX_MAX_VOLUME;
return(chunk);
775
776
}
777
778
779
/* Load a wave file of the mixer format from a memory buffer */
Mix_Chunk *Mix_QuickLoad_WAV(Uint8 *mem)
{
780
781
782
783
Mix_Chunk *chunk;
Uint8 magic[4];
/* Make sure audio has been opened */
784
if (! audio_opened) {
785
786
787
788
789
790
SDL_SetError("Audio device hasn't been opened");
return(NULL);
}
/* Allocate the chunk memory */
chunk = (Mix_Chunk *)SDL_calloc(1,sizeof(Mix_Chunk));
791
if (chunk == NULL) {
792
793
794
795
796
797
798
799
SDL_SetError("Out of memory");
return(NULL);
}
/* Essentially just skip to the audio data (no error checking - fast) */
chunk->allocated = 0;
mem += 12; /* WAV header */
do {
800
SDL_memcpy(magic, mem, 4);
801
802
803
804
805
mem += 4;
chunk->alen = ((mem[3]<<24)|(mem[2]<<16)|(mem[1]<<8)|(mem[0]));
mem += 4;
chunk->abuf = mem;
mem += chunk->alen;
806
} while (memcmp(magic, "data", 4) != 0);
807
808
809
chunk->volume = MIX_MAX_VOLUME;
return(chunk);
810
811
}
812
813
814
/* Load raw audio data of the mixer format from a memory buffer */
Mix_Chunk *Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len)
{
815
816
817
Mix_Chunk *chunk;
/* Make sure audio has been opened */
818
if (! audio_opened) {
819
820
821
822
823
824
SDL_SetError("Audio device hasn't been opened");
return(NULL);
}
/* Allocate the chunk memory */
chunk = (Mix_Chunk *)SDL_malloc(sizeof(Mix_Chunk));
825
if (chunk == NULL) {
826
827
828
829
830
831
832
833
834
835
836
SDL_SetError("Out of memory");
return(NULL);
}
/* Essentially just point at the audio data (no error checking - fast) */
chunk->allocated = 0;
chunk->alen = len;
chunk->abuf = mem;
chunk->volume = MIX_MAX_VOLUME;
return(chunk);
837
838
}
839
840
841
/* Free an audio chunk previously loaded */
void Mix_FreeChunk(Mix_Chunk *chunk)
{
842
843
844
int i;
/* Caution -- if the chunk is playing, the mixer will crash */
845
if (chunk) {
846
/* Guarantee that this chunk isn't playing */
847
Mix_LockAudio();
848
849
850
if (mix_channel) {
for (i=0; i<num_channels; ++i) {
if (chunk == mix_channel[i].chunk) {
851
852
853
854
855
mix_channel[i].playing = 0;
mix_channel[i].looping = 0;
}
}
}
856
Mix_UnlockAudio();
857
/* Actually free the chunk */
858
if (chunk->allocated) {
859
860
861
862
SDL_free(chunk->abuf);
}
SDL_free(chunk);
}
863
864
}
865
866
867
868
/* Set a function that is called after all mixing is performed.
This can be used to provide real-time visual display of the audio stream
or add a custom mixer filter for the stream data.
*/
869
void Mix_SetPostMix(void (SDLCALL *mix_func)
870
871
(void *udata, Uint8 *stream, int len), void *arg)
{
872
Mix_LockAudio();
873
874
mix_postmix_data = arg;
mix_postmix = mix_func;
875
Mix_UnlockAudio();
876
877
}
878
879
880
/* Add your own music player or mixer function.
If 'mix_func' is NULL, the default music player is re-enabled.
*/
881
void Mix_HookMusic(void (SDLCALL *mix_func)(void *udata, Uint8 *stream, int len),
882
883
void *arg)
{
884
Mix_LockAudio();
885
if (mix_func != NULL) {
886
887
888
889
890
891
music_data = arg;
mix_music = mix_func;
} else {
music_data = NULL;
mix_music = music_mixer;
}
892
Mix_UnlockAudio();
893
894
895
896
}
void *Mix_GetMusicHookData(void)
{
897
return(music_data);
898
899
}
900
void Mix_ChannelFinished(void (SDLCALL *channel_finished)(int channel))
901
{
902
Mix_LockAudio();
903
channel_done_callback = channel_finished;
904
Mix_UnlockAudio();
905
906
907
}
908
909
910
911
912
913
/* Reserve the first channels (0 -> n-1) for the application, i.e. don't allocate
them dynamically to the next sample if requested with a -1 value below.
Returns the number of reserved channels.
*/
int Mix_ReserveChannels(int num)
{
914
915
916
917
if (num > num_channels)
num = num_channels;
reserved_channels = num;
return num;
918
919
}
920
921
static int checkchunkintegral(Mix_Chunk *chunk)
{
922
int frame_width = 1;
923
924
925
926
927
if ((mixer.format & 0xFF) == 16) frame_width = 2;
frame_width *= mixer.channels;
while (chunk->alen % frame_width) chunk->alen--;
return chunk->alen;
928
929
}
930
931
/* Play an audio chunk on a specific channel.
If the specified channel is -1, play on the first free channel.
932
933
'ticks' is the number of milliseconds at most to play the sample, or -1
if there is no limit.
934
935
Returns which channel was used to play the sound.
*/
936
int Mix_PlayChannelTimed(int which, Mix_Chunk *chunk, int loops, int ticks)
937
{
938
939
940
int i;
/* Don't play null pointers :-) */
941
if (chunk == NULL) {
942
943
944
Mix_SetError("Tried to play a NULL chunk");
return(-1);
}
945
if (!checkchunkintegral(chunk)) {
946
947
948
949
950
Mix_SetError("Tried to play a chunk with a bad frame");
return(-1);
}
/* Lock the mixer while modifying the playing channels */
951
Mix_LockAudio();
952
953
{
/* If which is -1, play on the first free channel */
954
955
956
if (which == -1) {
for (i=reserved_channels; i<num_channels; ++i) {
if (mix_channel[i].playing <= 0)
957
958
break;
}
959
if (i == num_channels) {
960
961
962
963
964
965
966
967
Mix_SetError("No free channels available");
which = -1;
} else {
which = i;
}
}
/* Queue up the audio data for this channel */
968
if (which >= 0 && which < num_channels) {
969
970
971
972
973
974
975
976
977
978
979
980
981
Uint32 sdl_ticks = SDL_GetTicks();
if (Mix_Playing(which))
_Mix_channel_done_playing(which);
mix_channel[which].samples = chunk->abuf;
mix_channel[which].playing = chunk->alen;
mix_channel[which].looping = loops;
mix_channel[which].chunk = chunk;
mix_channel[which].paused = 0;
mix_channel[which].fading = MIX_NO_FADING;
mix_channel[which].start_time = sdl_ticks;
mix_channel[which].expire = (ticks>0) ? (sdl_ticks + ticks) : 0;
}
}
982
Mix_UnlockAudio();
983
984
985
/* Return the channel on which the sound is being played */
return(which);
986
987
}
988
989
990
/* Change the expiration delay for a channel */
int Mix_ExpireChannel(int which, int ticks)
{
991
992
int status = 0;
993
if (which == -1) {
994
int i;
995
for (i=0; i < num_channels; ++ i) {
996
997
status += Mix_ExpireChannel(i, ticks);
}
998
} else if (which < num_channels) {
999
Mix_LockAudio();
1000
mix_channel[which].expire = (ticks>0) ? (SDL_GetTicks() + ticks) : 0;