Subversion Repositories Games.Rick Dangerous

Rev

Rev 2 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * src/syssnd.c
  3.  *
  4.  * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net). All rights reserved.
  5.  *
  6.  * The use and distribution terms for this software are contained in the file
  7.  * named README, which can be found in the root of this distribution. By
  8.  * using this software in any fashion, you are agreeing to be bound by the
  9.  * terms of this license.
  10.  *
  11.  * You must not remove this notice, or any other, from this software.
  12.  */
  13.  
  14. #include <SDL.h>
  15. #include <stdlib.h>
  16. #include <memory.h>
  17.  
  18. #include "system.h"
  19. #include "game.h"
  20. #include "syssnd.h"
  21.  
  22.  
  23. #define ADJVOL(S) (((S) *sndVol) / SDL_MIX_MAXVOLUME)
  24.  
  25.  
  26. static U8 isAudioActive = FALSE;
  27. static channel_t channels[SYSSND_MIXCHANNELS];
  28.  
  29. static U8 sndVol = SDL_MIX_MAXVOLUME;   /* internal volume */
  30. static U8 sndUVol = SYSSND_MAXVOL;   /* user-selected volume */
  31. static U8 sndMute = FALSE;   /* mute flag */
  32.  
  33.  
  34. /*
  35.  * prototypes
  36.  */
  37. static void end_channel (channel_t *);
  38.  
  39.  
  40. /*
  41.  * Callback -- this is also where all sound mixing is done
  42.  *
  43.  * Note: it may not be that much a good idea to do all the mixing here ; it
  44.  * may be more efficient to mix samples every frame, or maybe everytime a
  45.  * new sound is sent to be played. I don't know.
  46.  */
  47. void syssnd_callback (void *userdata, U8 *stream, int len)
  48. {
  49.    channel_t *channel;
  50.    U8 channel_index;
  51.    S16 final_sample;
  52.    U32 i;
  53.  
  54.    //
  55.    for (i = 0; i < (U32)len; i += 2)
  56.    {
  57.       final_sample = 0;
  58.  
  59.       for (channel_index = 0; channel_index < SYSSND_MIXCHANNELS; channel_index++)
  60.       {
  61.          channel = &channels[channel_index]; // quick access to channel
  62.  
  63.          if (channel->loop == 0)
  64.             continue;
  65.  
  66.          // channel is active
  67.          if (channel->len > 0)
  68.          {
  69.             // not ending
  70.             final_sample += ADJVOL (*((U16 *) channel->buf));
  71.             channel->buf += 2;
  72.             channel->len -= 2;
  73.          }
  74.          else
  75.          {
  76.             // ending
  77.             if (channel->loop > 0)
  78.                channel->loop--;
  79.  
  80.             if (channel->loop)
  81.             {
  82.                // just loop
  83.                channel->buf = channel->snd->buf;
  84.                channel->len = channel->snd->len;
  85.  
  86.                final_sample += ADJVOL (*((U16 *) channel->buf));
  87.                channel->buf += 2;
  88.                channel->len -= 2;
  89.             }
  90.             else
  91.                end_channel (channel); // end for real
  92.          }
  93.       }
  94.  
  95.       *((S16 *) &stream[i]) = (sndMute ? 0 : final_sample);
  96.    }
  97. }
  98.  
  99.  
  100. static void end_channel (channel_t *channel)
  101. {
  102.    channel->loop = 0;
  103.    if (channel->snd->dispose)
  104.       syssnd_free (channel->snd);
  105.    channel->snd = NULL;
  106. }
  107.  
  108.  
  109. void syssnd_init (void)
  110. {
  111.    SDL_AudioSpec desired;
  112.    U16 c;
  113.  
  114.    if (SDL_InitSubSystem (SDL_INIT_AUDIO) < 0)
  115.       return;
  116.  
  117.    desired.freq = SYSSND_FREQ;
  118.    desired.format = AUDIO_S16;
  119.    desired.channels = SYSSND_CHANNELS;
  120.    desired.samples = SYSSND_MIXSAMPLES;
  121.    desired.callback = syssnd_callback;
  122.    desired.userdata = NULL;
  123.  
  124.    if (SDL_OpenAudio (&desired, NULL) < 0)
  125.       return;
  126.  
  127.    if (sysarg_args_vol != 0)
  128.    {
  129.       sndUVol = sysarg_args_vol;
  130.       sndVol = SDL_MIX_MAXVOLUME * sndUVol / SYSSND_MAXVOL;
  131.    }
  132.  
  133.    for (c = 0; c < SYSSND_MIXCHANNELS; c++)
  134.       channels[c].loop = 0;   /* deactivate */
  135.  
  136.    isAudioActive = TRUE;
  137.    SDL_PauseAudio (0);
  138. }
  139.  
  140.  
  141. /*
  142.  * Shutdown
  143.  */
  144. void syssnd_shutdown (void)
  145. {
  146.    if (!isAudioActive)
  147.       return;
  148.  
  149.    SDL_CloseAudio ();
  150.    isAudioActive = FALSE;
  151. }
  152.  
  153.  
  154. /*
  155.  * Toggle mute
  156.  *
  157.  * When muted, sounds are still managed but not sent to the dsp, hence
  158.  * it is possible to un-mute at any time.
  159.  */
  160. void syssnd_toggleMute (void)
  161. {
  162.    sndMute = !sndMute;
  163. }
  164.  
  165.  
  166. void syssnd_vol (S8 d)
  167. {
  168.    if ((d < 0 && sndUVol > 0) || (d > 0 && sndUVol < SYSSND_MAXVOL))
  169.    {
  170.       sndUVol += d;
  171.       sndVol = SDL_MIX_MAXVOLUME * sndUVol / SYSSND_MAXVOL;
  172.    }
  173. }
  174.  
  175.  
  176. /*
  177.  * Play a sound
  178.  *
  179.  * loop: number of times the sound should be played, -1 to loop forever
  180.  * returns: channel number, or -1 if none was available
  181.  *
  182.  * NOTE if sound is already playing, simply reset it (i.e. can not have
  183.  * twice the same sound playing -- tends to become noisy when too many
  184.  * bad guys die at the same time).
  185.  */
  186. S8 syssnd_play (sound_t *sound, S8 loop)
  187. {
  188.    S8 channel_index;
  189.  
  190.    if ((sound == NULL) || !isAudioActive)
  191.       return (-1);
  192.  
  193.    for (channel_index = 0; channel_index < SYSSND_MIXCHANNELS; channel_index++)
  194.       if ((channels[channel_index].snd == sound) || (channels[channel_index].loop == 0))
  195.          break; // look for a free channel slot
  196.    if (channel_index == SYSSND_MIXCHANNELS)
  197.       return (-1);
  198.  
  199.    channels[channel_index].loop = loop;
  200.    channels[channel_index].snd = sound;
  201.    channels[channel_index].buf = sound->buf;
  202.    channels[channel_index].len = sound->len;
  203.  
  204.    return (channel_index);
  205. }
  206.  
  207.  
  208. /*
  209.  * Pause
  210.  *
  211.  * pause: TRUE or FALSE
  212.  * clear: TRUE to cleanup all sounds and make sure we start from scratch
  213.  */
  214. void syssnd_pause (U8 want_pause, U8 want_clear)
  215. {
  216.    U8 channel_index;
  217.  
  218.    if (!isAudioActive)
  219.       return;
  220.  
  221.    if (want_clear)
  222.       for (channel_index = 0; channel_index < SYSSND_MIXCHANNELS; channel_index++)
  223.          channels[channel_index].loop = 0;
  224.  
  225.    SDL_PauseAudio (want_pause ? 1 : 0);
  226. }
  227.  
  228.  
  229. /*
  230.  * Stop a channel
  231.  */
  232. void syssnd_stopchan (S8 chan)
  233. {
  234.    if (chan < 0 || chan > SYSSND_MIXCHANNELS)
  235.       return;
  236.  
  237.    if (channels[chan].snd)
  238.       end_channel (&channels[chan]);
  239. }
  240.  
  241.  
  242. /*
  243.  * Stop a sound
  244.  */
  245. void syssnd_stopsound (sound_t *sound)
  246. {
  247.    U8 i;
  248.  
  249.    if (!sound)
  250.       return;
  251.  
  252.    for (i = 0; i < SYSSND_MIXCHANNELS; i++)
  253.       if (channels[i].snd == sound)
  254.          end_channel (&channels[i]);
  255. }
  256.  
  257.  
  258. /*
  259.  * See if a sound is playing
  260.  */
  261. int syssnd_isplaying (sound_t *sound)
  262. {
  263.    U8 i;
  264.  
  265.    for (i = 0; i < SYSSND_MIXCHANNELS; i++)
  266.       if (channels[i].snd == sound)
  267.          return (1);
  268.  
  269.    return (0);
  270. }
  271.  
  272.  
  273. /*
  274.  * Stops all channels.
  275.  */
  276. void syssnd_stopall (void)
  277. {
  278.    U8 i;
  279.  
  280.    for (i = 0; i < SYSSND_MIXCHANNELS; i++)
  281.       if (channels[i].snd)
  282.          end_channel (&channels[i]);
  283. }
  284.  
  285.  
  286. /*
  287.  * Load a sound.
  288.  */
  289. sound_t *syssnd_load (char *name)
  290. {
  291.    char *soundfile_fullpathname;
  292.    sound_t *s;
  293.    SDL_AudioSpec audiospec;
  294.  
  295.    /* alloc space for sound file's full pathname and build it */
  296.    soundfile_fullpathname = (char *) malloc (1024);
  297.    sprintf_s (soundfile_fullpathname, 1024, "%s/%s", sys_getbasepath (), name);
  298.  
  299.    /* alloc sound */
  300.    s = malloc (sizeof (sound_t));
  301.  
  302.    /* read */
  303.    /* second param == 1 -> close source once read */
  304.    if (!SDL_LoadWAV_RW (SDL_RWFromFile (soundfile_fullpathname, "rb"), 1, &audiospec, &(s->buf), &(s->len)))
  305.    {
  306.       free (s);
  307.       return NULL;
  308.    }
  309.    free (soundfile_fullpathname);
  310.  
  311.    s->dispose = FALSE;
  312.  
  313.    return s;
  314. }
  315.  
  316.  
  317. /*
  318.  *
  319.  */
  320. void syssnd_free (sound_t *s)
  321. {
  322.    if (!s)
  323.       return;
  324.  
  325.    if (s->buf)
  326.       SDL_FreeWAV (s->buf);
  327.  
  328.    s->buf = NULL;
  329.    s->len = 0;
  330. }
  331.