mixer.c
上传用户:nini_0081
上传日期:2022-07-21
资源大小:2628k
文件大小:34k
源码类别:

多媒体编程

开发平台:

DOS

  1. /*
  2.     SDL_mixer:  An audio mixer library based on the SDL library
  3.     Copyright (C) 1997-2009 Sam Lantinga
  4.     This library is free software; you can redistribute it and/or
  5.     modify it under the terms of the GNU Library General Public
  6.     License as published by the Free Software Foundation; either
  7.     version 2 of the License, or (at your option) any later version.
  8.     This library is distributed in the hope that it will be useful,
  9.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11.     Library General Public License for more details.
  12.     You should have received a copy of the GNU Library General Public
  13.     License along with this library; if not, write to the Free
  14.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  15.     Sam Lantinga
  16.     slouken@libsdl.org
  17. */
  18. /* $Id: mixer.c 5243 2009-11-14 19:31:39Z slouken $ */
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include "SDL_mutex.h"
  23. #include "SDL_endian.h"
  24. #include "SDL_timer.h"
  25. #include "SDL_mixer.h"
  26. #include "load_aiff.h"
  27. #include "load_voc.h"
  28. #include "load_ogg.h"
  29. #include "load_flac.h"
  30. #include "dynamic_flac.h"
  31. #include "dynamic_mod.h"
  32. #include "dynamic_mp3.h"
  33. #include "dynamic_ogg.h"
  34. #define __MIX_INTERNAL_EFFECT__
  35. #include "effects_internal.h"
  36. /* Magic numbers for various audio file formats */
  37. #define RIFF 0x46464952 /* "RIFF" */
  38. #define WAVE 0x45564157 /* "WAVE" */
  39. #define FORM 0x4d524f46 /* "FORM" */
  40. #define OGGS 0x5367674f /* "OggS" */
  41. #define CREA 0x61657243 /* "Crea" */
  42. #define FLAC 0x43614C66 /* "fLaC" */
  43. static int audio_opened = 0;
  44. static SDL_AudioSpec mixer;
  45. typedef struct _Mix_effectinfo
  46. {
  47. Mix_EffectFunc_t callback;
  48. Mix_EffectDone_t done_callback;
  49. void *udata;
  50. struct _Mix_effectinfo *next;
  51. } effect_info;
  52. static struct _Mix_Channel {
  53. Mix_Chunk *chunk;
  54. int playing;
  55. int paused;
  56. Uint8 *samples;
  57. int volume;
  58. int looping;
  59. int tag;
  60. Uint32 expire;
  61. Uint32 start_time;
  62. Mix_Fading fading;
  63. int fade_volume;
  64. int fade_volume_reset;
  65. Uint32 fade_length;
  66. Uint32 ticks_fade;
  67. effect_info *effects;
  68. } *mix_channel = NULL;
  69. static effect_info *posteffects = NULL;
  70. static int num_channels;
  71. static int reserved_channels = 0;
  72. /* Support for hooking into the mixer callback system */
  73. static void (*mix_postmix)(void *udata, Uint8 *stream, int len) = NULL;
  74. static void *mix_postmix_data = NULL;
  75. /* rcg07062001 callback to alert when channels are done playing. */
  76. static void (*channel_done_callback)(int channel) = NULL;
  77. /* Music function declarations */
  78. extern int open_music(SDL_AudioSpec *mixer);
  79. extern void close_music(void);
  80. /* Support for user defined music functions, plus the default one */
  81. extern int volatile music_active;
  82. extern void music_mixer(void *udata, Uint8 *stream, int len);
  83. static void (*mix_music)(void *udata, Uint8 *stream, int len) = music_mixer;
  84. static void *music_data = NULL;
  85. /* rcg06042009 report available decoders at runtime. */
  86. static const char **chunk_decoders = NULL;
  87. static int num_decoders = 0;
  88. int Mix_GetNumChunkDecoders(void)
  89. {
  90. return(num_decoders);
  91. }
  92. const char *Mix_GetChunkDecoder(int index)
  93. {
  94. if ((index < 0) || (index >= num_decoders)) {
  95. return NULL;
  96. }
  97. return(chunk_decoders[index]);
  98. }
  99. static void add_chunk_decoder(const char *decoder)
  100. {
  101. void *ptr = realloc(chunk_decoders, (num_decoders + 1) * sizeof (const char **));
  102. if (ptr == NULL) {
  103. return;  /* oh well, go on without it. */
  104. }
  105. chunk_decoders = (const char **) ptr;
  106. chunk_decoders[num_decoders++] = decoder;
  107. }
  108. /* rcg06192001 get linked library's version. */
  109. const SDL_version *Mix_Linked_Version(void)
  110. {
  111. static SDL_version linked_version;
  112. SDL_MIXER_VERSION(&linked_version);
  113. return(&linked_version);
  114. }
  115. static int initialized = 0;
  116. int Mix_Init(int flags)
  117. {
  118. int result = 0;
  119. if (flags & MIX_INIT_FLAC) {
  120. #ifdef FLAC_MUSIC
  121. if ((initialized & MIX_INIT_FLAC) || Mix_InitFLAC() == 0) {
  122. result |= MIX_INIT_FLAC;
  123. }
  124. #else
  125. Mix_SetError("Mixer not built with FLAC support");
  126. #endif
  127. }
  128. if (flags & MIX_INIT_MOD) {
  129. #ifdef MOD_MUSIC
  130. if ((initialized & MIX_INIT_MOD) || Mix_InitMOD() == 0) {
  131. result |= MIX_INIT_MOD;
  132. }
  133. #else
  134. Mix_SetError("Mixer not built with MOD support");
  135. #endif
  136. }
  137. if (flags & MIX_INIT_MP3) {
  138. #ifdef MP3_MUSIC
  139. if ((initialized & MIX_INIT_MP3) || Mix_InitMP3() == 0) {
  140. result |= MIX_INIT_MP3;
  141. }
  142. #else
  143. Mix_SetError("Mixer not built with MP3 support");
  144. #endif
  145. }
  146. if (flags & MIX_INIT_OGG) {
  147. #ifdef OGG_MUSIC
  148. if ((initialized & MIX_INIT_OGG) || Mix_InitOgg() == 0) {
  149. result |= MIX_INIT_OGG;
  150. }
  151. #else
  152. Mix_SetError("Mixer not built with Ogg Vorbis support");
  153. #endif
  154. }
  155. initialized |= result;
  156. return (result);
  157. }
  158. void Mix_Quit()
  159. {
  160. #ifdef FLAC_MUSIC
  161. if (initialized & MIX_INIT_FLAC) {
  162. Mix_QuitFLAC();
  163. }
  164. #endif
  165. #ifdef MOD_MUSIC
  166. if (initialized & MIX_INIT_MOD) {
  167. Mix_QuitMOD();
  168. }
  169. #endif
  170. #ifdef MP3_MUSIC
  171. if (initialized & MIX_INIT_MP3) {
  172. Mix_QuitMP3();
  173. }
  174. #endif
  175. #ifdef OGG_MUSIC
  176. if (initialized & MIX_INIT_OGG) {
  177. Mix_QuitOgg();
  178. }
  179. #endif
  180. initialized = 0;
  181. }
  182. static int _Mix_remove_all_effects(int channel, effect_info **e);
  183. /*
  184.  * rcg06122001 Cleanup effect callbacks.
  185.  *  MAKE SURE SDL_LockAudio() is called before this (or you're in the
  186.  *   audio callback).
  187.  */
  188. static void _Mix_channel_done_playing(int channel)
  189. {
  190. if (channel_done_callback) {
  191.     channel_done_callback(channel);
  192. }
  193. /*
  194.  * Call internal function directly, to avoid locking audio from
  195.  *   inside audio callback.
  196.  */
  197. _Mix_remove_all_effects(channel, &mix_channel[channel].effects);
  198. }
  199. static void *Mix_DoEffects(int chan, void *snd, int len)
  200. {
  201. int posteffect = (chan == MIX_CHANNEL_POST);
  202. effect_info *e = ((posteffect) ? posteffects : mix_channel[chan].effects);
  203. void *buf = snd;
  204. if (e != NULL) {    /* are there any registered effects? */
  205. /* if this is the postmix, we can just overwrite the original. */
  206. if (!posteffect) {
  207. buf = malloc(len);
  208. if (buf == NULL) {
  209. return(snd);
  210. }
  211.     memcpy(buf, snd, len);
  212. }
  213. for (; e != NULL; e = e->next) {
  214. if (e->callback != NULL) {
  215. e->callback(chan, buf, len, e->udata);
  216. }
  217. }
  218. }
  219. /* be sure to free() the return value if != snd ... */
  220. return(buf);
  221. }
  222. /* Mixing function */
  223. static void mix_channels(void *udata, Uint8 *stream, int len)
  224. {
  225. Uint8 *mix_input;
  226. int i, mixable, volume = SDL_MIX_MAXVOLUME;
  227. Uint32 sdl_ticks;
  228. #if SDL_VERSION_ATLEAST(1, 3, 0)
  229. /* Need to initialize the stream in SDL 1.3+ */
  230. memset(stream, mixer.silence, len);
  231. #endif
  232. /* Mix the music (must be done before the channels are added) */
  233. if ( music_active || (mix_music != music_mixer) ) {
  234. mix_music(music_data, stream, len);
  235. }
  236. /* Mix any playing channels... */
  237. sdl_ticks = SDL_GetTicks();
  238. for ( i=0; i<num_channels; ++i ) {
  239. if( ! mix_channel[i].paused ) {
  240. if ( mix_channel[i].expire > 0 && mix_channel[i].expire < sdl_ticks ) {
  241. /* Expiration delay for that channel is reached */
  242. mix_channel[i].playing = 0;
  243. mix_channel[i].fading = MIX_NO_FADING;
  244. mix_channel[i].expire = 0;
  245. _Mix_channel_done_playing(i);
  246. } else if ( mix_channel[i].fading != MIX_NO_FADING ) {
  247. Uint32 ticks = sdl_ticks - mix_channel[i].ticks_fade;
  248. if( ticks > mix_channel[i].fade_length ) {
  249.     Mix_Volume(i, mix_channel[i].fade_volume_reset); /* Restore the volume */
  250. if( mix_channel[i].fading == MIX_FADING_OUT ) {
  251. mix_channel[i].playing = 0;
  252. mix_channel[i].expire = 0;
  253. _Mix_channel_done_playing(i);
  254. }
  255. mix_channel[i].fading = MIX_NO_FADING;
  256. } else {
  257. if( mix_channel[i].fading == MIX_FADING_OUT ) {
  258. Mix_Volume(i, (mix_channel[i].fade_volume * (mix_channel[i].fade_length-ticks))
  259.    / mix_channel[i].fade_length );
  260. } else {
  261. Mix_Volume(i, (mix_channel[i].fade_volume * ticks) / mix_channel[i].fade_length );
  262. }
  263. }
  264. }
  265. if ( mix_channel[i].playing > 0 ) {
  266. int index = 0;
  267. int remaining = len;
  268. while (mix_channel[i].playing > 0 && index < len) {
  269. remaining = len - index;
  270. volume = (mix_channel[i].volume*mix_channel[i].chunk->volume) / MIX_MAX_VOLUME;
  271. mixable = mix_channel[i].playing;
  272. if ( mixable > remaining ) {
  273. mixable = remaining;
  274. }
  275. mix_input = Mix_DoEffects(i, mix_channel[i].samples, mixable);
  276. SDL_MixAudio(stream+index,mix_input,mixable,volume);
  277. if (mix_input != mix_channel[i].samples)
  278. free(mix_input);
  279. mix_channel[i].samples += mixable;
  280. mix_channel[i].playing -= mixable;
  281. index += mixable;
  282. /* rcg06072001 Alert app if channel is done playing. */
  283. if (!mix_channel[i].playing && !mix_channel[i].looping) {
  284. _Mix_channel_done_playing(i);
  285. }
  286. }
  287. /* If looping the sample and we are at its end, make sure
  288.    we will still return a full buffer */
  289. while ( mix_channel[i].looping && index < len ) {
  290. int alen = mix_channel[i].chunk->alen;
  291. remaining = len - index;
  292. if (remaining > alen) {
  293. remaining = alen;
  294. }
  295. mix_input = Mix_DoEffects(i, mix_channel[i].chunk->abuf, remaining);
  296. SDL_MixAudio(stream+index, mix_input, remaining, volume);
  297. if (mix_input != mix_channel[i].chunk->abuf)
  298. free(mix_input);
  299. --mix_channel[i].looping;
  300. mix_channel[i].samples = mix_channel[i].chunk->abuf + remaining;
  301. mix_channel[i].playing = mix_channel[i].chunk->alen - remaining;
  302. index += remaining;
  303. }
  304. if ( ! mix_channel[i].playing && mix_channel[i].looping ) {
  305. --mix_channel[i].looping;
  306. mix_channel[i].samples = mix_channel[i].chunk->abuf;
  307. mix_channel[i].playing = mix_channel[i].chunk->alen;
  308. }
  309. }
  310. }
  311. }
  312. /* rcg06122001 run posteffects... */
  313. Mix_DoEffects(MIX_CHANNEL_POST, stream, len);
  314. if ( mix_postmix ) {
  315. mix_postmix(mix_postmix_data, stream, len);
  316. }
  317. }
  318. #if 0
  319. static void PrintFormat(char *title, SDL_AudioSpec *fmt)
  320. {
  321. printf("%s: %d bit %s audio (%s) at %u Hzn", title, (fmt->format&0xFF),
  322. (fmt->format&0x8000) ? "signed" : "unsigned",
  323. (fmt->channels > 2) ? "surround" :
  324. (fmt->channels > 1) ? "stereo" : "mono", fmt->freq);
  325. }
  326. #endif
  327. /* Open the mixer with a certain desired audio format */
  328. int Mix_OpenAudio(int frequency, Uint16 format, int nchannels, int chunksize)
  329. {
  330. int i;
  331. SDL_AudioSpec desired;
  332. /* If the mixer is already opened, increment open count */
  333. if ( audio_opened ) {
  334. if ( format == mixer.format && nchannels == mixer.channels ) {
  335.      ++audio_opened;
  336.      return(0);
  337. }
  338. while ( audio_opened ) {
  339. Mix_CloseAudio();
  340. }
  341. }
  342. /* Set the desired format and frequency */
  343. desired.freq = frequency;
  344. desired.format = format;
  345. desired.channels = nchannels;
  346. desired.samples = chunksize;
  347. desired.callback = mix_channels;
  348. desired.userdata = NULL;
  349. /* Accept nearly any audio format */
  350. if ( SDL_OpenAudio(&desired, &mixer) < 0 ) {
  351. return(-1);
  352. }
  353. #if 0
  354. PrintFormat("Audio device", &mixer);
  355. #endif
  356. /* Initialize the music players */
  357. if ( open_music(&mixer) < 0 ) {
  358. SDL_CloseAudio();
  359. return(-1);
  360. }
  361. num_channels = MIX_CHANNELS;
  362. mix_channel = (struct _Mix_Channel *) malloc(num_channels * sizeof(struct _Mix_Channel));
  363. /* Clear out the audio channels */
  364. for ( i=0; i<num_channels; ++i ) {
  365. mix_channel[i].chunk = NULL;
  366. mix_channel[i].playing = 0;
  367. mix_channel[i].looping = 0;
  368. mix_channel[i].volume = SDL_MIX_MAXVOLUME;
  369. mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME;
  370. mix_channel[i].fade_volume_reset = SDL_MIX_MAXVOLUME;
  371. mix_channel[i].fading = MIX_NO_FADING;
  372. mix_channel[i].tag = -1;
  373. mix_channel[i].expire = 0;
  374. mix_channel[i].effects = NULL;
  375. mix_channel[i].paused = 0;
  376. }
  377. Mix_VolumeMusic(SDL_MIX_MAXVOLUME);
  378. _Mix_InitEffects();
  379. /* This list is (currently) decided at build time. */
  380. add_chunk_decoder("WAVE");
  381. add_chunk_decoder("AIFF");
  382. add_chunk_decoder("VOC");
  383. #ifdef OGG_MUSIC
  384. add_chunk_decoder("OGG");
  385. #endif
  386. #ifdef FLAC_MUSIC
  387. add_chunk_decoder("FLAC");
  388. #endif
  389. audio_opened = 1;
  390. SDL_PauseAudio(0);
  391. return(0);
  392. }
  393. /* Dynamically change the number of channels managed by the mixer.
  394.    If decreasing the number of channels, the upper channels are
  395.    stopped.
  396.  */
  397. int Mix_AllocateChannels(int numchans)
  398. {
  399. if ( numchans<0 || numchans==num_channels )
  400. return(num_channels);
  401. if ( numchans < num_channels ) {
  402. /* Stop the affected channels */
  403. int i;
  404. for(i=numchans; i < num_channels; i++) {
  405. Mix_UnregisterAllEffects(i);
  406. Mix_HaltChannel(i);
  407. }
  408. }
  409. SDL_LockAudio();
  410. mix_channel = (struct _Mix_Channel *) realloc(mix_channel, numchans * sizeof(struct _Mix_Channel));
  411. if ( numchans > num_channels ) {
  412. /* Initialize the new channels */
  413. int i;
  414. for(i=num_channels; i < numchans; i++) {
  415. mix_channel[i].chunk = NULL;
  416. mix_channel[i].playing = 0;
  417. mix_channel[i].looping = 0;
  418. mix_channel[i].volume = SDL_MIX_MAXVOLUME;
  419. mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME;
  420. mix_channel[i].fade_volume_reset = SDL_MIX_MAXVOLUME;
  421. mix_channel[i].fading = MIX_NO_FADING;
  422. mix_channel[i].tag = -1;
  423. mix_channel[i].expire = 0;
  424. mix_channel[i].effects = NULL;
  425. mix_channel[i].paused = 0;
  426. }
  427. }
  428. num_channels = numchans;
  429. SDL_UnlockAudio();
  430. return(num_channels);
  431. }
  432. /* Return the actual mixer parameters */
  433. int Mix_QuerySpec(int *frequency, Uint16 *format, int *channels)
  434. {
  435. if ( audio_opened ) {
  436. if ( frequency ) {
  437. *frequency = mixer.freq;
  438. }
  439. if ( format ) {
  440. *format = mixer.format;
  441. }
  442. if ( channels ) {
  443. *channels = mixer.channels;
  444. }
  445. }
  446. return(audio_opened);
  447. }
  448. /*
  449.  * !!! FIXME: Ideally, we want a Mix_LoadSample_RW(), which will handle the
  450.  *             generic setup, then call the correct file format loader.
  451.  */
  452. /* Load a wave file */
  453. Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc)
  454. {
  455. Uint32 magic;
  456. Mix_Chunk *chunk;
  457. SDL_AudioSpec wavespec, *loaded;
  458. SDL_AudioCVT wavecvt;
  459. int samplesize;
  460. /* rcg06012001 Make sure src is valid */
  461. if ( ! src ) {
  462. SDL_SetError("Mix_LoadWAV_RW with NULL src");
  463. return(NULL);
  464. }
  465. /* Make sure audio has been opened */
  466. if ( ! audio_opened ) {
  467. SDL_SetError("Audio device hasn't been opened");
  468. if ( freesrc && src ) {
  469. SDL_RWclose(src);
  470. }
  471. return(NULL);
  472. }
  473. /* Allocate the chunk memory */
  474. chunk = (Mix_Chunk *)malloc(sizeof(Mix_Chunk));
  475. if ( chunk == NULL ) {
  476. SDL_SetError("Out of memory");
  477. if ( freesrc ) {
  478. SDL_RWclose(src);
  479. }
  480. return(NULL);
  481. }
  482. /* Find out what kind of audio file this is */
  483. magic = SDL_ReadLE32(src);
  484. /* Seek backwards for compatibility with older loaders */
  485. SDL_RWseek(src, -(int)sizeof(Uint32), RW_SEEK_CUR);
  486. switch (magic) {
  487. case WAVE:
  488. case RIFF:
  489. loaded = SDL_LoadWAV_RW(src, freesrc, &wavespec,
  490. (Uint8 **)&chunk->abuf, &chunk->alen);
  491. break;
  492. case FORM:
  493. loaded = Mix_LoadAIFF_RW(src, freesrc, &wavespec,
  494. (Uint8 **)&chunk->abuf, &chunk->alen);
  495. break;
  496. #ifdef OGG_MUSIC
  497. case OGGS:
  498. loaded = Mix_LoadOGG_RW(src, freesrc, &wavespec,
  499. (Uint8 **)&chunk->abuf, &chunk->alen);
  500. break;
  501. #endif
  502. #ifdef FLAC_MUSIC
  503. case FLAC:
  504. loaded = Mix_LoadFLAC_RW(src, freesrc, &wavespec,
  505. (Uint8 **)&chunk->abuf, &chunk->alen);
  506. break;
  507. #endif
  508. case CREA:
  509. loaded = Mix_LoadVOC_RW(src, freesrc, &wavespec,
  510. (Uint8 **)&chunk->abuf, &chunk->alen);
  511. break;
  512. default:
  513. SDL_SetError("Unrecognized sound file type");
  514. return(0);
  515. }
  516. if ( !loaded ) {
  517. free(chunk);
  518. return(NULL);
  519. }
  520. #if 0
  521. PrintFormat("Audio device", &mixer);
  522. PrintFormat("-- Wave file", &wavespec);
  523. #endif
  524. /* Build the audio converter and create conversion buffers */
  525. if ( SDL_BuildAudioCVT(&wavecvt,
  526. wavespec.format, wavespec.channels, wavespec.freq,
  527. mixer.format, mixer.channels, mixer.freq) < 0 ) {
  528. SDL_FreeWAV(chunk->abuf);
  529. free(chunk);
  530. return(NULL);
  531. }
  532. samplesize = ((wavespec.format & 0xFF)/8)*wavespec.channels;
  533. wavecvt.len = chunk->alen & ~(samplesize-1);
  534. wavecvt.buf = (Uint8 *)malloc(wavecvt.len*wavecvt.len_mult);
  535. if ( wavecvt.buf == NULL ) {
  536. SDL_SetError("Out of memory");
  537. SDL_FreeWAV(chunk->abuf);
  538. free(chunk);
  539. return(NULL);
  540. }
  541. memcpy(wavecvt.buf, chunk->abuf, chunk->alen);
  542. SDL_FreeWAV(chunk->abuf);
  543. /* Run the audio converter */
  544. if ( SDL_ConvertAudio(&wavecvt) < 0 ) {
  545. free(wavecvt.buf);
  546. free(chunk);
  547. return(NULL);
  548. }
  549. chunk->allocated = 1;
  550. chunk->abuf = wavecvt.buf;
  551. chunk->alen = wavecvt.len_cvt;
  552. chunk->volume = MIX_MAX_VOLUME;
  553. return(chunk);
  554. }
  555. /* Load a wave file of the mixer format from a memory buffer */
  556. Mix_Chunk *Mix_QuickLoad_WAV(Uint8 *mem)
  557. {
  558. Mix_Chunk *chunk;
  559. Uint8 magic[4];
  560. /* Make sure audio has been opened */
  561. if ( ! audio_opened ) {
  562. SDL_SetError("Audio device hasn't been opened");
  563. return(NULL);
  564. }
  565. /* Allocate the chunk memory */
  566. chunk = (Mix_Chunk *)calloc(1,sizeof(Mix_Chunk));
  567. if ( chunk == NULL ) {
  568. SDL_SetError("Out of memory");
  569. return(NULL);
  570. }
  571. /* Essentially just skip to the audio data (no error checking - fast) */
  572. chunk->allocated = 0;
  573. mem += 12; /* WAV header */
  574. do {
  575. memcpy(magic, mem, 4);
  576. mem += 4;
  577. chunk->alen = ((mem[3]<<24)|(mem[2]<<16)|(mem[1]<<8)|(mem[0]));
  578. mem += 4;
  579. chunk->abuf = mem;
  580. mem += chunk->alen;
  581. } while ( memcmp(magic, "data", 4) != 0 );
  582. chunk->volume = MIX_MAX_VOLUME;
  583. return(chunk);
  584. }
  585. /* Load raw audio data of the mixer format from a memory buffer */
  586. Mix_Chunk *Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len)
  587. {
  588. Mix_Chunk *chunk;
  589. /* Make sure audio has been opened */
  590. if ( ! audio_opened ) {
  591. SDL_SetError("Audio device hasn't been opened");
  592. return(NULL);
  593. }
  594. /* Allocate the chunk memory */
  595. chunk = (Mix_Chunk *)malloc(sizeof(Mix_Chunk));
  596. if ( chunk == NULL ) {
  597. SDL_SetError("Out of memory");
  598. return(NULL);
  599. }
  600. /* Essentially just point at the audio data (no error checking - fast) */
  601. chunk->allocated = 0;
  602. chunk->alen = len;
  603. chunk->abuf = mem;
  604. chunk->volume = MIX_MAX_VOLUME;
  605. return(chunk);
  606. }
  607. /* Free an audio chunk previously loaded */
  608. void Mix_FreeChunk(Mix_Chunk *chunk)
  609. {
  610. int i;
  611. /* Caution -- if the chunk is playing, the mixer will crash */
  612. if ( chunk ) {
  613. /* Guarantee that this chunk isn't playing */
  614. SDL_LockAudio();
  615. if ( mix_channel ) {
  616. for ( i=0; i<num_channels; ++i ) {
  617. if ( chunk == mix_channel[i].chunk ) {
  618. mix_channel[i].playing = 0;
  619. }
  620. }
  621. }
  622. SDL_UnlockAudio();
  623. /* Actually free the chunk */
  624. if ( chunk->allocated ) {
  625. free(chunk->abuf);
  626. }
  627. free(chunk);
  628. }
  629. }
  630. /* Set a function that is called after all mixing is performed.
  631.    This can be used to provide real-time visual display of the audio stream
  632.    or add a custom mixer filter for the stream data.
  633. */
  634. void Mix_SetPostMix(void (*mix_func)
  635.                     (void *udata, Uint8 *stream, int len), void *arg)
  636. {
  637. SDL_LockAudio();
  638. mix_postmix_data = arg;
  639. mix_postmix = mix_func;
  640. SDL_UnlockAudio();
  641. }
  642. /* Add your own music player or mixer function.
  643.    If 'mix_func' is NULL, the default music player is re-enabled.
  644.  */
  645. void Mix_HookMusic(void (*mix_func)(void *udata, Uint8 *stream, int len),
  646.                                                                 void *arg)
  647. {
  648. SDL_LockAudio();
  649. if ( mix_func != NULL ) {
  650. music_data = arg;
  651. mix_music = mix_func;
  652. } else {
  653. music_data = NULL;
  654. mix_music = music_mixer;
  655. }
  656. SDL_UnlockAudio();
  657. }
  658. void *Mix_GetMusicHookData(void)
  659. {
  660. return(music_data);
  661. }
  662. void Mix_ChannelFinished(void (*channel_finished)(int channel))
  663. {
  664. SDL_LockAudio();
  665. channel_done_callback = channel_finished;
  666. SDL_UnlockAudio();
  667. }
  668. /* Reserve the first channels (0 -> n-1) for the application, i.e. don't allocate
  669.    them dynamically to the next sample if requested with a -1 value below.
  670.    Returns the number of reserved channels.
  671.  */
  672. int Mix_ReserveChannels(int num)
  673. {
  674. if (num > num_channels)
  675. num = num_channels;
  676. reserved_channels = num;
  677. return num;
  678. }
  679. static int checkchunkintegral(Mix_Chunk *chunk)
  680. {
  681. int frame_width = 1;
  682. if ((mixer.format & 0xFF) == 16) frame_width = 2;
  683. frame_width *= mixer.channels;
  684. while (chunk->alen % frame_width) chunk->alen--;
  685. return chunk->alen;
  686. }
  687. /* Play an audio chunk on a specific channel.
  688.    If the specified channel is -1, play on the first free channel.
  689.    'ticks' is the number of milliseconds at most to play the sample, or -1
  690.    if there is no limit.
  691.    Returns which channel was used to play the sound.
  692. */
  693. int Mix_PlayChannelTimed(int which, Mix_Chunk *chunk, int loops, int ticks)
  694. {
  695. int i;
  696. /* Don't play null pointers :-) */
  697. if ( chunk == NULL ) {
  698. Mix_SetError("Tried to play a NULL chunk");
  699. return(-1);
  700. }
  701. if ( !checkchunkintegral(chunk)) {
  702. Mix_SetError("Tried to play a chunk with a bad frame");
  703. return(-1);
  704. }
  705. /* Lock the mixer while modifying the playing channels */
  706. SDL_LockAudio();
  707. {
  708. /* If which is -1, play on the first free channel */
  709. if ( which == -1 ) {
  710. for ( i=reserved_channels; i<num_channels; ++i ) {
  711. if ( mix_channel[i].playing <= 0 )
  712. break;
  713. }
  714. if ( i == num_channels ) {
  715. Mix_SetError("No free channels available");
  716. which = -1;
  717. } else {
  718. which = i;
  719. }
  720. }
  721. /* Queue up the audio data for this channel */
  722. if ( which >= 0 ) {
  723. Uint32 sdl_ticks = SDL_GetTicks();
  724. if (Mix_Playing(which))
  725. _Mix_channel_done_playing(which);
  726. mix_channel[which].samples = chunk->abuf;
  727. mix_channel[which].playing = chunk->alen;
  728. mix_channel[which].looping = loops;
  729. mix_channel[which].chunk = chunk;
  730. mix_channel[which].paused = 0;
  731. mix_channel[which].fading = MIX_NO_FADING;
  732. mix_channel[which].start_time = sdl_ticks;
  733. mix_channel[which].expire = (ticks>0) ? (sdl_ticks + ticks) : 0;
  734. }
  735. }
  736. SDL_UnlockAudio();
  737. /* Return the channel on which the sound is being played */
  738. return(which);
  739. }
  740. /* Change the expiration delay for a channel */
  741. int Mix_ExpireChannel(int which, int ticks)
  742. {
  743. int status = 0;
  744. if ( which == -1 ) {
  745. int i;
  746. for ( i=0; i < num_channels; ++ i ) {
  747. status += Mix_ExpireChannel(i, ticks);
  748. }
  749. } else if ( which < num_channels ) {
  750. SDL_LockAudio();
  751. mix_channel[which].expire = (ticks>0) ? (SDL_GetTicks() + ticks) : 0;
  752. SDL_UnlockAudio();
  753. ++ status;
  754. }
  755. return(status);
  756. }
  757. /* Fade in a sound on a channel, over ms milliseconds */
  758. int Mix_FadeInChannelTimed(int which, Mix_Chunk *chunk, int loops, int ms, int ticks)
  759. {
  760. int i;
  761. /* Don't play null pointers :-) */
  762. if ( chunk == NULL ) {
  763. return(-1);
  764. }
  765. if ( !checkchunkintegral(chunk)) {
  766. Mix_SetError("Tried to play a chunk with a bad frame");
  767. return(-1);
  768. }
  769. /* Lock the mixer while modifying the playing channels */
  770. SDL_LockAudio();
  771. {
  772. /* If which is -1, play on the first free channel */
  773. if ( which == -1 ) {
  774. for ( i=reserved_channels; i<num_channels; ++i ) {
  775. if ( mix_channel[i].playing <= 0 )
  776. break;
  777. }
  778. if ( i == num_channels ) {
  779. which = -1;
  780. } else {
  781. which = i;
  782. }
  783. }
  784. /* Queue up the audio data for this channel */
  785. if ( which >= 0 ) {
  786. Uint32 sdl_ticks = SDL_GetTicks();
  787. if (Mix_Playing(which))
  788. _Mix_channel_done_playing(which);
  789. mix_channel[which].samples = chunk->abuf;
  790. mix_channel[which].playing = chunk->alen;
  791. mix_channel[which].looping = loops;
  792. mix_channel[which].chunk = chunk;
  793. mix_channel[which].paused = 0;
  794. mix_channel[which].fading = MIX_FADING_IN;
  795. mix_channel[which].fade_volume = mix_channel[which].volume;
  796. mix_channel[which].fade_volume_reset = mix_channel[which].volume;
  797. mix_channel[which].volume = 0;
  798. mix_channel[which].fade_length = (Uint32)ms;
  799. mix_channel[which].start_time = mix_channel[which].ticks_fade = sdl_ticks;
  800. mix_channel[which].expire = (ticks > 0) ? (sdl_ticks+ticks) : 0;
  801. }
  802. }
  803. SDL_UnlockAudio();
  804. /* Return the channel on which the sound is being played */
  805. return(which);
  806. }
  807. /* Set volume of a particular channel */
  808. int Mix_Volume(int which, int volume)
  809. {
  810. int i;
  811. int prev_volume;
  812. if ( which == -1 ) {
  813. prev_volume = 0;
  814. for ( i=0; i<num_channels; ++i ) {
  815. prev_volume += Mix_Volume(i, volume);
  816. }
  817. prev_volume /= num_channels;
  818. } else {
  819. prev_volume = mix_channel[which].volume;
  820. if ( volume >= 0 ) {
  821. if ( volume > SDL_MIX_MAXVOLUME ) {
  822. volume = SDL_MIX_MAXVOLUME;
  823. }
  824. mix_channel[which].volume = volume;
  825. }
  826. }
  827. return(prev_volume);
  828. }
  829. /* Set volume of a particular chunk */
  830. int Mix_VolumeChunk(Mix_Chunk *chunk, int volume)
  831. {
  832. int prev_volume;
  833. prev_volume = chunk->volume;
  834. if ( volume >= 0 ) {
  835. if ( volume > MIX_MAX_VOLUME ) {
  836. volume = MIX_MAX_VOLUME;
  837. }
  838. chunk->volume = volume;
  839. }
  840. return(prev_volume);
  841. }
  842. /* Halt playing of a particular channel */
  843. int Mix_HaltChannel(int which)
  844. {
  845. int i;
  846. if ( which == -1 ) {
  847. for ( i=0; i<num_channels; ++i ) {
  848. Mix_HaltChannel(i);
  849. }
  850. } else {
  851. SDL_LockAudio();
  852. if (mix_channel[which].playing) {
  853. _Mix_channel_done_playing(which);
  854. mix_channel[which].playing = 0;
  855. }
  856. mix_channel[which].expire = 0;
  857. if(mix_channel[which].fading != MIX_NO_FADING) /* Restore volume */
  858. mix_channel[which].volume = mix_channel[which].fade_volume_reset;
  859. mix_channel[which].fading = MIX_NO_FADING;
  860. SDL_UnlockAudio();
  861. }
  862. return(0);
  863. }
  864. /* Halt playing of a particular group of channels */
  865. int Mix_HaltGroup(int tag)
  866. {
  867. int i;
  868. for ( i=0; i<num_channels; ++i ) {
  869. if( mix_channel[i].tag == tag ) {
  870. Mix_HaltChannel(i);
  871. }
  872. }
  873. return(0);
  874. }
  875. /* Fade out a channel and then stop it automatically */
  876. int Mix_FadeOutChannel(int which, int ms)
  877. {
  878. int status;
  879. status = 0;
  880. if ( audio_opened ) {
  881. if ( which == -1 ) {
  882. int i;
  883. for ( i=0; i<num_channels; ++i ) {
  884. status += Mix_FadeOutChannel(i, ms);
  885. }
  886. } else {
  887. SDL_LockAudio();
  888. if ( mix_channel[which].playing && 
  889.     (mix_channel[which].volume > 0) &&
  890.     (mix_channel[which].fading != MIX_FADING_OUT) ) {
  891. mix_channel[which].fade_volume = mix_channel[which].volume;
  892. mix_channel[which].fading = MIX_FADING_OUT;
  893. mix_channel[which].fade_length = ms;
  894. mix_channel[which].ticks_fade = SDL_GetTicks();
  895. /* only change fade_volume_reset if we're not fading. */
  896. if (mix_channel[which].fading == MIX_NO_FADING) {
  897.     mix_channel[which].fade_volume_reset = mix_channel[which].volume;
  898. }
  899. ++status;
  900. }
  901. SDL_UnlockAudio();
  902. }
  903. }
  904. return(status);
  905. }
  906. /* Halt playing of a particular group of channels */
  907. int Mix_FadeOutGroup(int tag, int ms)
  908. {
  909. int i;
  910. int status = 0;
  911. for ( i=0; i<num_channels; ++i ) {
  912. if( mix_channel[i].tag == tag ) {
  913. status += Mix_FadeOutChannel(i,ms);
  914. }
  915. }
  916. return(status);
  917. }
  918. Mix_Fading Mix_FadingChannel(int which)
  919. {
  920. return mix_channel[which].fading;
  921. }
  922. /* Check the status of a specific channel.
  923.    If the specified mix_channel is -1, check all mix channels.
  924. */
  925. int Mix_Playing(int which)
  926. {
  927. int status;
  928. status = 0;
  929. if ( which == -1 ) {
  930. int i;
  931. for ( i=0; i<num_channels; ++i ) {
  932. if ((mix_channel[i].playing > 0) ||
  933. (mix_channel[i].looping > 0))
  934. {
  935. ++status;
  936. }
  937. }
  938. } else {
  939. if ((mix_channel[which].playing > 0) ||
  940. (mix_channel[which].looping > 0))
  941. {
  942. ++status;
  943. }
  944. }
  945. return(status);
  946. }
  947. /* rcg06072001 Get the chunk associated with a channel. */
  948. Mix_Chunk *Mix_GetChunk(int channel)
  949. {
  950. Mix_Chunk *retval = NULL;
  951. if ((channel >= 0) && (channel < num_channels)) {
  952. retval = mix_channel[channel].chunk;
  953. }
  954. return(retval);
  955. }
  956. /* Close the mixer, halting all playing audio */
  957. void Mix_CloseAudio(void)
  958. {
  959. int i;
  960. if ( audio_opened ) {
  961. if ( audio_opened == 1 ) {
  962. for (i = 0; i < num_channels; i++) {
  963. Mix_UnregisterAllEffects(i);
  964. }
  965. Mix_UnregisterAllEffects(MIX_CHANNEL_POST);
  966. close_music();
  967. Mix_HaltChannel(-1);
  968. _Mix_DeinitEffects();
  969. SDL_CloseAudio();
  970. free(mix_channel);
  971. mix_channel = NULL;
  972. /* rcg06042009 report available decoders at runtime. */
  973. free(chunk_decoders);
  974. chunk_decoders = NULL;
  975. num_decoders = 0;
  976. }
  977. --audio_opened;
  978. }
  979. }
  980. /* Pause a particular channel (or all) */
  981. void Mix_Pause(int which)
  982. {
  983. Uint32 sdl_ticks = SDL_GetTicks();
  984. if ( which == -1 ) {
  985. int i;
  986. for ( i=0; i<num_channels; ++i ) {
  987. if ( mix_channel[i].playing > 0 ) {
  988. mix_channel[i].paused = sdl_ticks;
  989. }
  990. }
  991. } else {
  992. if ( mix_channel[which].playing > 0 ) {
  993. mix_channel[which].paused = sdl_ticks;
  994. }
  995. }
  996. }
  997. /* Resume a paused channel */
  998. void Mix_Resume(int which)
  999. {
  1000. Uint32 sdl_ticks = SDL_GetTicks();
  1001. SDL_LockAudio();
  1002. if ( which == -1 ) {
  1003. int i;
  1004. for ( i=0; i<num_channels; ++i ) {
  1005. if ( mix_channel[i].playing > 0 ) {
  1006. if(mix_channel[i].expire > 0)
  1007. mix_channel[i].expire += sdl_ticks - mix_channel[i].paused;
  1008. mix_channel[i].paused = 0;
  1009. }
  1010. }
  1011. } else {
  1012. if ( mix_channel[which].playing > 0 ) {
  1013. if(mix_channel[which].expire > 0)
  1014. mix_channel[which].expire += sdl_ticks - mix_channel[which].paused;
  1015. mix_channel[which].paused = 0;
  1016. }
  1017. }
  1018. SDL_UnlockAudio();
  1019. }
  1020. int Mix_Paused(int which)
  1021. {
  1022. if ( which > num_channels )
  1023. return(0);
  1024. if ( which < 0 ) {
  1025. int status = 0;
  1026. int i;
  1027. for( i=0; i < num_channels; ++i ) {
  1028. if ( mix_channel[i].paused ) {
  1029. ++ status;
  1030. }
  1031. }
  1032. return(status);
  1033. } else {
  1034. return(mix_channel[which].paused != 0);
  1035. }
  1036. }
  1037. /* Change the group of a channel */
  1038. int Mix_GroupChannel(int which, int tag)
  1039. {
  1040. if ( which < 0 || which > num_channels )
  1041. return(0);
  1042. SDL_LockAudio();
  1043. mix_channel[which].tag = tag;
  1044. SDL_UnlockAudio();
  1045. return(1);
  1046. }
  1047. /* Assign several consecutive channels to a group */
  1048. int Mix_GroupChannels(int from, int to, int tag)
  1049. {
  1050. int status = 0;
  1051. for( ; from <= to; ++ from ) {
  1052. status += Mix_GroupChannel(from, tag);
  1053. }
  1054. return(status);
  1055. }
  1056. /* Finds the first available channel in a group of channels */
  1057. int Mix_GroupAvailable(int tag)
  1058. {
  1059. int i;
  1060. for( i=0; i < num_channels; i ++ ) {
  1061. if ( ((tag == -1) || (tag == mix_channel[i].tag)) &&
  1062.                     (mix_channel[i].playing <= 0) )
  1063. return i;
  1064. }
  1065. return(-1);
  1066. }
  1067. int Mix_GroupCount(int tag)
  1068. {
  1069. int count = 0;
  1070. int i;
  1071. for( i=0; i < num_channels; i ++ ) {
  1072. if ( mix_channel[i].tag==tag || tag==-1 )
  1073. ++ count;
  1074. }
  1075. return(count);
  1076. }
  1077. /* Finds the "oldest" sample playing in a group of channels */
  1078. int Mix_GroupOldest(int tag)
  1079. {
  1080. int chan = -1;
  1081. Uint32 mintime = SDL_GetTicks();
  1082. int i;
  1083. for( i=0; i < num_channels; i ++ ) {
  1084. if ( (mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0
  1085.  && mix_channel[i].start_time <= mintime ) {
  1086. mintime = mix_channel[i].start_time;
  1087. chan = i;
  1088. }
  1089. }
  1090. return(chan);
  1091. }
  1092. /* Finds the "most recent" (i.e. last) sample playing in a group of channels */
  1093. int Mix_GroupNewer(int tag)
  1094. {
  1095. int chan = -1;
  1096. Uint32 maxtime = 0;
  1097. int i;
  1098. for( i=0; i < num_channels; i ++ ) {
  1099. if ( (mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0
  1100.  && mix_channel[i].start_time >= maxtime ) {
  1101. maxtime = mix_channel[i].start_time;
  1102. chan = i;
  1103. }
  1104. }
  1105. return(chan);
  1106. }
  1107. /*
  1108.  * rcg06122001 The special effects exportable API.
  1109.  *  Please see effect_*.c for internally-implemented effects, such
  1110.  *  as Mix_SetPanning().
  1111.  */
  1112. /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
  1113. static int _Mix_register_effect(effect_info **e, Mix_EffectFunc_t f,
  1114. Mix_EffectDone_t d, void *arg)
  1115. {
  1116. effect_info *new_e = malloc(sizeof (effect_info));
  1117. if (!e) {
  1118. Mix_SetError("Internal error");
  1119. return(0);
  1120. }
  1121. if (f == NULL) {
  1122. Mix_SetError("NULL effect callback");
  1123. return(0);
  1124. }
  1125. if (new_e == NULL) {
  1126. Mix_SetError("Out of memory");
  1127. return(0);
  1128. }
  1129. new_e->callback = f;
  1130. new_e->done_callback = d;
  1131. new_e->udata = arg;
  1132. new_e->next = NULL;
  1133. /* add new effect to end of linked list... */
  1134. if (*e == NULL) {
  1135. *e = new_e;
  1136. } else {
  1137. effect_info *cur = *e;
  1138. while (1) {
  1139. if (cur->next == NULL) {
  1140. cur->next = new_e;
  1141. break;
  1142. }
  1143. cur = cur->next;
  1144. }
  1145. }
  1146. return(1);
  1147. }
  1148. /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
  1149. static int _Mix_remove_effect(int channel, effect_info **e, Mix_EffectFunc_t f)
  1150. {
  1151. effect_info *cur;
  1152. effect_info *prev = NULL;
  1153. effect_info *next = NULL;
  1154. if (!e) {
  1155. Mix_SetError("Internal error");
  1156. return(0);
  1157. }
  1158. for (cur = *e; cur != NULL; cur = cur->next) {
  1159. if (cur->callback == f) {
  1160. next = cur->next;
  1161. if (cur->done_callback != NULL) {
  1162. cur->done_callback(channel, cur->udata);
  1163. }
  1164. free(cur);
  1165. if (prev == NULL) {   /* removing first item of list? */
  1166. *e = next;
  1167. } else {
  1168. prev->next = next;
  1169. }
  1170. return(1);
  1171. }
  1172. prev = cur;
  1173. }
  1174. Mix_SetError("No such effect registered");
  1175. return(0);
  1176. }
  1177. /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
  1178. static int _Mix_remove_all_effects(int channel, effect_info **e)
  1179. {
  1180. effect_info *cur;
  1181. effect_info *next;
  1182. if (!e) {
  1183. Mix_SetError("Internal error");
  1184. return(0);
  1185. }
  1186. for (cur = *e; cur != NULL; cur = next) {
  1187. next = cur->next;
  1188. if (cur->done_callback != NULL) {
  1189. cur->done_callback(channel, cur->udata);
  1190. }
  1191. free(cur);
  1192. }
  1193. *e = NULL;
  1194. return(1);
  1195. }
  1196. /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
  1197. int _Mix_RegisterEffect_locked(int channel, Mix_EffectFunc_t f,
  1198. Mix_EffectDone_t d, void *arg)
  1199. {
  1200. effect_info **e = NULL;
  1201. if (channel == MIX_CHANNEL_POST) {
  1202. e = &posteffects;
  1203. } else {
  1204. if ((channel < 0) || (channel >= num_channels)) {
  1205. Mix_SetError("Invalid channel number");
  1206. return(0);
  1207. }
  1208. e = &mix_channel[channel].effects;
  1209. }
  1210. return _Mix_register_effect(e, f, d, arg);
  1211. }
  1212. int Mix_RegisterEffect(int channel, Mix_EffectFunc_t f,
  1213. Mix_EffectDone_t d, void *arg)
  1214. {
  1215.     int retval;
  1216. SDL_LockAudio();
  1217. retval = _Mix_RegisterEffect_locked(channel, f, d, arg);
  1218. SDL_UnlockAudio();
  1219.     return retval;
  1220. }
  1221. /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
  1222. int _Mix_UnregisterEffect_locked(int channel, Mix_EffectFunc_t f)
  1223. {
  1224. effect_info **e = NULL;
  1225. if (channel == MIX_CHANNEL_POST) {
  1226. e = &posteffects;
  1227. } else {
  1228. if ((channel < 0) || (channel >= num_channels)) {
  1229. Mix_SetError("Invalid channel number");
  1230. return(0);
  1231. }
  1232. e = &mix_channel[channel].effects;
  1233. }
  1234. return _Mix_remove_effect(channel, e, f);
  1235. }
  1236. int Mix_UnregisterEffect(int channel, Mix_EffectFunc_t f)
  1237. {
  1238. int retval;
  1239. SDL_LockAudio();
  1240. retval = _Mix_UnregisterEffect_locked(channel, f);
  1241. SDL_UnlockAudio();
  1242. return(retval);
  1243. }
  1244. /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
  1245. int _Mix_UnregisterAllEffects_locked(int channel)
  1246. {
  1247. effect_info **e = NULL;
  1248. if (channel == MIX_CHANNEL_POST) {
  1249. e = &posteffects;
  1250. } else {
  1251. if ((channel < 0) || (channel >= num_channels)) {
  1252. Mix_SetError("Invalid channel number");
  1253. return(0);
  1254. }
  1255. e = &mix_channel[channel].effects;
  1256. }
  1257. return _Mix_remove_all_effects(channel, e);
  1258. }
  1259. int Mix_UnregisterAllEffects(int channel)
  1260. {
  1261. int retval;
  1262. SDL_LockAudio();
  1263. retval = _Mix_UnregisterAllEffects_locked(channel);
  1264. SDL_UnlockAudio();
  1265. return(retval);
  1266. }
  1267. /* end of mixer.c ... */