Subversion Repositories Games.Rick Dangerous

Rev

Rev 1 | Rev 5 | 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 channel[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. static SDL_mutex *sndlock;
  34.  
  35.  
  36. /*
  37.  * prototypes
  38.  */
  39. static int sdlRWops_open (SDL_RWops *context, char *name);
  40. static int sdlRWops_seek (SDL_RWops *context, int offset, int whence);
  41. static int sdlRWops_read (SDL_RWops *context, void *ptr, int size, int maxnum);
  42. static int sdlRWops_write (SDL_RWops *context, const void *ptr, int size, int num);
  43. static int sdlRWops_close (SDL_RWops *context);
  44. static void end_channel (U8);
  45.  
  46.  
  47. /*
  48.  * Callback -- this is also where all sound mixing is done
  49.  *
  50.  * Note: it may not be that much a good idea to do all the mixing here ; it
  51.  * may be more efficient to mix samples every frame, or maybe everytime a
  52.  * new sound is sent to be played. I don't know.
  53.  */
  54. void syssnd_callback (void *userdata, U8 *stream, int len)
  55. {
  56.    U8 c;
  57.    S16 s;
  58.    U32 i;
  59.  
  60.    SDL_mutexP(sndlock);
  61.  
  62.    for (i = 0; i < (U32)len; i++)
  63.    {
  64.       s = 0;
  65.       for (c = 0; c < SYSSND_MIXCHANNELS; c++)
  66.       {
  67.          if (channel[c].loop != 0)
  68.          {
  69.             /* channel is active */
  70.             if (channel[c].len > 0)
  71.             {
  72.                /* not ending */
  73.                s += ADJVOL (*channel[c].buf - 0x80);
  74.                channel[c].buf++;
  75.                channel[c].len--;
  76.             }
  77.             else
  78.             {
  79.                /* ending */
  80.                if (channel[c].loop > 0)
  81.                   channel[c].loop--;
  82.  
  83.                if (channel[c].loop)
  84.                {
  85.                   /* just loop */
  86.                   channel[c].buf = channel[c].snd->buf;
  87.                   channel[c].len = channel[c].snd->len;
  88.                   s += ADJVOL (*channel[c].buf - 0x80);
  89.                   channel[c].buf++;
  90.                   channel[c].len--;
  91.                }
  92.                else
  93.                   end_channel(c); /* end for real */
  94.             }
  95.          }
  96.       }
  97.  
  98.       if (sndMute)
  99.          stream[i] = 0x80;
  100.       else
  101.       {
  102.          s += 0x80;
  103.          if (s > 0xff) s = 0xff;
  104.          if (s < 0x00) s = 0x00;
  105.          stream[i] = (U8) s;
  106.       }
  107.    }
  108.  
  109.    memcpy (stream, stream, len);
  110.  
  111.    SDL_mutexV (sndlock);
  112. }
  113.  
  114.  
  115. static void end_channel (U8 c)
  116. {
  117.    channel[c].loop = 0;
  118.    if (channel[c].snd->dispose)
  119.       syssnd_free (channel[c].snd);
  120.    channel[c].snd = NULL;
  121. }
  122.  
  123.  
  124. void syssnd_init (void)
  125. {
  126.    SDL_AudioSpec desired, obtained;
  127.    U16 c;
  128.  
  129.    if (SDL_InitSubSystem (SDL_INIT_AUDIO) < 0)
  130.       return;
  131.  
  132.    desired.freq = SYSSND_FREQ;
  133.    desired.format = AUDIO_U8;
  134.    desired.channels = SYSSND_CHANNELS;
  135.    desired.samples = SYSSND_MIXSAMPLES;
  136.    desired.callback = syssnd_callback;
  137.    desired.userdata = NULL;
  138.  
  139.    if (SDL_OpenAudio (&desired, &obtained) < 0)
  140.       return;
  141.  
  142.    sndlock = SDL_CreateMutex ();
  143.    if (sndlock == NULL)
  144.    {
  145.       SDL_CloseAudio ();
  146.       return;
  147.    }
  148.  
  149.    if (sysarg_args_vol != 0)
  150.    {
  151.       sndUVol = sysarg_args_vol;
  152.       sndVol = SDL_MIX_MAXVOLUME * sndUVol / SYSSND_MAXVOL;
  153.    }
  154.  
  155.    for (c = 0; c < SYSSND_MIXCHANNELS; c++)
  156.       channel[c].loop = 0;   /* deactivate */
  157.  
  158.    isAudioActive = TRUE;
  159.    SDL_PauseAudio (0);
  160. }
  161.  
  162.  
  163. /*
  164.  * Shutdown
  165.  */
  166. void syssnd_shutdown (void)
  167. {
  168.    if (!isAudioActive)
  169.       return;
  170.  
  171.    SDL_CloseAudio ();
  172.    SDL_DestroyMutex (sndlock);
  173.    isAudioActive = FALSE;
  174. }
  175.  
  176.  
  177. /*
  178.  * Toggle mute
  179.  *
  180.  * When muted, sounds are still managed but not sent to the dsp, hence
  181.  * it is possible to un-mute at any time.
  182.  */
  183. void syssnd_toggleMute (void)
  184. {
  185.    SDL_mutexP (sndlock);
  186.    sndMute = !sndMute;
  187.    SDL_mutexV (sndlock);
  188. }
  189.  
  190.  
  191. void syssnd_vol (S8 d)
  192. {
  193.    if ((d < 0 && sndUVol > 0) || (d > 0 && sndUVol < SYSSND_MAXVOL))
  194.    {
  195.       sndUVol += d;
  196.       SDL_mutexP (sndlock);
  197.       sndVol = SDL_MIX_MAXVOLUME * sndUVol / SYSSND_MAXVOL;
  198.       SDL_mutexV (sndlock);
  199.    }
  200. }
  201.  
  202.  
  203. /*
  204.  * Play a sound
  205.  *
  206.  * loop: number of times the sound should be played, -1 to loop forever
  207.  * returns: channel number, or -1 if none was available
  208.  *
  209.  * NOTE if sound is already playing, simply reset it (i.e. can not have
  210.  * twice the same sound playing -- tends to become noisy when too many
  211.  * bad guys die at the same time).
  212.  */
  213. S8 syssnd_play (sound_t *sound, S8 loop)
  214. {
  215.    S8 c;
  216.  
  217.    if (!isAudioActive)
  218.       return -1;
  219.    if (sound == NULL)
  220.       return -1;
  221.  
  222.    c = 0;
  223.    SDL_mutexP (sndlock);
  224.    while ((channel[c].snd != sound || channel[c].loop == 0) && channel[c].loop != 0 && c < SYSSND_MIXCHANNELS)
  225.       c++;
  226.    if (c == SYSSND_MIXCHANNELS)
  227.       c = -1;
  228.  
  229.    if (c >= 0)
  230.    {
  231.       channel[c].loop = loop;
  232.       channel[c].snd = sound;
  233.       channel[c].buf = sound->buf;
  234.       channel[c].len = sound->len;
  235.    }
  236.  
  237.    SDL_mutexV (sndlock);
  238.  
  239.    return c;
  240. }
  241.  
  242.  
  243. /*
  244.  * Pause
  245.  *
  246.  * pause: TRUE or FALSE
  247.  * clear: TRUE to cleanup all sounds and make sure we start from scratch
  248.  */
  249. void syssnd_pause (U8 pause, U8 clear)
  250. {
  251.    U8 c;
  252.  
  253.    if (!isAudioActive)
  254.       return;
  255.  
  256.    if (clear == TRUE)
  257.    {
  258.       SDL_mutexP (sndlock);
  259.       for (c = 0; c < SYSSND_MIXCHANNELS; c++)
  260.          channel[c].loop = 0;
  261.       SDL_mutexV (sndlock);
  262.    }
  263.  
  264.    if (pause == TRUE)
  265.       SDL_PauseAudio (1);
  266.    else
  267.       SDL_PauseAudio (0);
  268. }
  269.  
  270.  
  271. /*
  272.  * Stop a channel
  273.  */
  274. void syssnd_stopchan (S8 chan)
  275. {
  276.    if (chan < 0 || chan > SYSSND_MIXCHANNELS)
  277.       return;
  278.  
  279.    SDL_mutexP (sndlock);
  280.  
  281.    if (channel[chan].snd)
  282.       end_channel (chan);
  283.  
  284.    SDL_mutexV (sndlock);
  285. }
  286.  
  287.  
  288. /*
  289.  * Stop a sound
  290.  */
  291. void syssnd_stopsound (sound_t *sound)
  292. {
  293.    U8 i;
  294.  
  295.    if (!sound)
  296.       return;
  297.  
  298.    SDL_mutexP (sndlock);
  299.  
  300.    for (i = 0; i < SYSSND_MIXCHANNELS; i++)
  301.       if (channel[i].snd == sound)
  302.          end_channel (i);
  303.  
  304.    SDL_mutexV (sndlock);
  305. }
  306.  
  307.  
  308. /*
  309.  * See if a sound is playing
  310.  */
  311. int syssnd_isplaying (sound_t *sound)
  312. {
  313.    U8 i, playing;
  314.  
  315.    playing = 0;
  316.    SDL_mutexP (sndlock);
  317.  
  318.    for (i = 0; i < SYSSND_MIXCHANNELS; i++)
  319.       if (channel[i].snd == sound)
  320.          playing = 1;
  321.  
  322.    SDL_mutexV (sndlock);
  323.    return playing;
  324. }
  325.  
  326.  
  327. /*
  328.  * Stops all channels.
  329.  */
  330. void syssnd_stopall (void)
  331. {
  332.    U8 i;
  333.  
  334.    SDL_mutexP (sndlock);
  335.  
  336.    for (i = 0; i < SYSSND_MIXCHANNELS; i++)
  337.       if (channel[i].snd)
  338.          end_channel (i);
  339.  
  340.    SDL_mutexV (sndlock);
  341. }
  342.  
  343.  
  344. /*
  345.  * Load a sound.
  346.  */
  347. sound_t *syssnd_load (char *name)
  348. {
  349.    char *soundfile_fullpathname;
  350.    sound_t *s;
  351.    SDL_RWops *context;
  352.    SDL_AudioSpec audiospec;
  353.  
  354.    /* alloc space for sound file's full pathname and build it */
  355.    soundfile_fullpathname = (char *) malloc (1024);
  356.    sprintf_s (soundfile_fullpathname, 1024, "%s/%s", sys_getbasepath (), name);
  357.  
  358.    /* alloc context */
  359.    context = malloc (sizeof (SDL_RWops));
  360.    context->seek = sdlRWops_seek;
  361.    context->read = sdlRWops_read;
  362.    context->write = sdlRWops_write;
  363.    context->close = sdlRWops_close;
  364.  
  365.    /* open */
  366.    if (sdlRWops_open (context, soundfile_fullpathname) == -1)
  367.    {
  368.       free (soundfile_fullpathname);
  369.       return NULL;
  370.    }
  371.    free (soundfile_fullpathname);
  372.  
  373.    /* alloc sound */
  374.    s = malloc (sizeof (sound_t));
  375.  
  376.    /* read */
  377.    /* second param == 1 -> close source once read */
  378.    if (!SDL_LoadWAV_RW (context, 1, &audiospec, &(s->buf), &(s->len)))
  379.    {
  380.       free (s);
  381.       return NULL;
  382.    }
  383.  
  384.    s->dispose = FALSE;
  385.  
  386.    return s;
  387. }
  388.  
  389.  
  390. /*
  391.  *
  392.  */
  393. void syssnd_free (sound_t *s)
  394. {
  395.    if (!s)
  396.       return;
  397.  
  398.    if (s->buf)
  399.       SDL_FreeWAV (s->buf);
  400.  
  401.    s->buf = NULL;
  402.    s->len = 0;
  403. }
  404.  
  405.  
  406. /*
  407.  *
  408.  */
  409. static int sdlRWops_open (SDL_RWops *context, char *name)
  410. {
  411.    FILE *fp;
  412.  
  413.    fopen_s (&fp, name, "rb");
  414.  
  415.    if (!fp)
  416.       return -1;
  417.  
  418.    context->hidden.unknown.data1 = (void *) fp;
  419.  
  420.    return 0;
  421. }
  422.  
  423.  
  424. static int sdlRWops_seek (SDL_RWops *context, int offset, int whence)
  425. {
  426.    return (fseek ((FILE *) context->hidden.unknown.data1, offset, whence));
  427. }
  428.  
  429.  
  430. static int sdlRWops_read (SDL_RWops *context, void *ptr, int size, int maxnum)
  431. {
  432.    return (fread (ptr, size, maxnum, (FILE *) context->hidden.unknown.data1));
  433. }
  434.  
  435.  
  436. static int sdlRWops_write (SDL_RWops *context, const void *ptr, int size, int num)
  437. {
  438.    return -1; /* not implemented */
  439. }
  440.  
  441.  
  442. static int sdlRWops_close (SDL_RWops *context)
  443. {
  444.    if (context)
  445.    {
  446.       fclose ((FILE *) context->hidden.unknown.data1);
  447.       free (context);
  448.    }
  449.  
  450.    return 0;
  451. }
  452.